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
unit_csv.cpp
Go to the documentation of this file.
1 // -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*-
2 
3 #include "unit_generic.h"
4 #include "csv.h"
5 #include "savegame.h"
6 #include "xml_serializer.h"
7 #include "gfx/sphere.h"
8 #include "unit_collide.h"
9 #include "collide2/Stdafx.h"
11 #include "unit_factory.h"
12 #include "audiolib.h"
13 #include "unit_xml.h"
14 #include "gfx/quaternion.h"
15 #include "role_bitmask.h"
16 #include "unit_csv.h"
17 #include <algorithm>
18 #include "lin_time.h"
19 #include "unit_const_cache.h"
20 #define VS_PI 3.1415926535897931
21 
22 CSVRow LookupUnitRow( const string &unitname, const string &faction )
23 {
24  string hashname = unitname+"__"+faction;
25  for (vector< CSVTable* >::reverse_iterator i = unitTables.rbegin(); i != unitTables.rend(); ++i) {
26  unsigned int where;
27  if ( (*i)->RowExists( hashname, where ) )
28  return CSVRow( (*i), where );
29  else if ( (*i)->RowExists( unitname, where ) )
30  return CSVRow( (*i), where );
31  }
32  return CSVRow();
33 }
34 
35 extern int GetModeFromName( const char *input_buffer );
36 extern void pushMesh( std::vector< Mesh* >&mesh,
37  float &randomstartframe,
38  float &randomstartseconds,
39  const char *filename,
40  const float scale,
41  int faction,
42  class Flightgroup*fg,
43  int startframe,
44  double texturestarttime );
45 void addShieldMesh( Unit::XML*xml, const char *filename, const float scale, int faction, class Flightgroup*fg );
46 void addRapidMesh( Unit::XML*xml, const char *filename, const float scale, int faction, class Flightgroup*fg );
47 
48 static void UpgradeUnit( Unit *un, const std::string &upgrades )
49 {
50  string::size_type when;
51  string::size_type ofs = 0;
52  while ( ( when = upgrades.find( '{', ofs ) ) != string::npos ) {
53  string::size_type where = upgrades.find( '}', when+1 );
54  string upgrade = upgrades.substr( when+1, ( (where == string::npos) ? string::npos : where-when-1 ) );
55  ofs = ( (where == string::npos) ? string::npos : where+1 );
56 
57  unsigned int mountoffset = 0;
58  unsigned int subunitoffset = 0;
59  string::size_type where1 = upgrade.find( ';' );
60  string::size_type where2 = upgrade.rfind( ';' );
61  if (where1 != string::npos) {
62  mountoffset = XMLSupport::parse_int( upgrade.substr( where1+1, where2 != where1 ? where2 : upgrade.length() ) );
63  if (where2 != where1 && where2 != string::npos)
64  subunitoffset = XMLSupport::parse_int( upgrade.substr( where2+1 ) );
65  }
66  upgrade = upgrade.substr( 0, where1 );
67  if (upgrade.length() == 0)
68  continue;
70  if (!upgradee) {
72  UnitFactory::createUnit( upgrade.c_str(),
73  true,
75  }
76  double percent = 1.0;
77  un->Unit::Upgrade( upgradee,
78  mountoffset,
79  subunitoffset,
80  GetModeFromName( upgrade.c_str() ), true, percent, NULL );
81  }
82 }
83 
84 void AddMeshes( std::vector< Mesh* > &xmeshes,
85  float &randomstartframe,
86  float &randomstartseconds,
87  float unitscale,
88  const std::string &meshes,
89  int faction,
90  Flightgroup *fg,
91  vector< unsigned int > *counts )
92 {
93  string::size_type where, when, wheresf, wherest, ofs = 0;
94  if (counts) counts->clear();
95  {
96  int nelem = 0;
97  while ( ( ofs = meshes.find( '{', ofs ) ) != string::npos )
98  nelem++, ofs++;
99  if (counts) counts->reserve( nelem );
100  xmeshes.reserve( nelem );
101  ofs = 0;
102  }
103  while ( ( where = meshes.find( '{', ofs ) ) != string::npos ) {
104  when = meshes.find( '}', where+1 ); //matching closing brace
105  string mesh = meshes.substr( where+1, ( (when == string::npos) ? string::npos : when-where-1 ) );
106  ofs = ( (when == string::npos) ? string::npos : when+1 );
107 
108  wheresf = mesh.find( ';' );
109  string startf = "0";
110  string startt = "0";
111  if (wheresf != string::npos) {
112  startf = mesh.substr( wheresf+1 );
113  mesh = mesh.substr( 0, wheresf );
114  wherest = startf.find( ';' );
115  if (wherest != string::npos) {
116  startt = startf.substr( wherest+1 );
117  startf = startf.substr( 0, wherest );
118  }
119  }
120  int startframe = startf == "RANDOM" ? -1 : ( startf == "ASYNC" ? -1 : atoi( startf.c_str() ) );
121  float starttime = startt == "RANDOM" ? -1.0f : atof( startt.c_str() );
122  unsigned int s = xmeshes.size();
123  pushMesh( xmeshes, randomstartframe, randomstartseconds, mesh.c_str(), unitscale, faction, fg, startframe, starttime );
124  if (counts) counts->push_back( xmeshes.size()-s );
125  }
126 }
127 
128 static std::pair< string::size_type, string::size_type >nextElementRange( const string &inp,
129  string::size_type &start,
130  string::size_type end )
131 {
132  string::size_type ostart = start;
133  start = inp.find( ';', start );
134  if ( start != string::npos && ( start != end && (end == string::npos || start < end) ) ) {
135  ++start;
136  return std::pair< string::size_type, string::size_type > ( ostart, start-1 );
137  } else {
138  start = end;
139  return std::pair< string::size_type, string::size_type > ( ostart, end );
140  }
141 }
142 
143 static string nextElementString( const string &inp, string::size_type &start, string::size_type end )
144 {
145  std::pair< string::size_type, string::size_type >rng = nextElementRange( inp, start, end );
146  if (rng.second == string::npos)
147  return inp.substr( rng.first );
148 
149  else
150  return inp.substr( rng.first, rng.second-rng.first );
151 }
152 
153 static int nextElementInt( const string &inp, string::size_type &start, string::size_type end, int def = 0 )
154 {
155  std::pair< string::size_type, string::size_type >rng = nextElementRange( inp, start, end );
156  return (rng.first == rng.second) ? def : atoi( inp.c_str()+rng.first );
157 }
158 
159 static double nextElementFloat( const string &inp, string::size_type &start, string::size_type end, double def = 0 )
160 {
161  std::pair< string::size_type, string::size_type >rng = nextElementRange( inp, start, end );
162  return (rng.first == rng.second) ? def : atof( inp.c_str()+rng.first );
163 }
164 
165 static double nextElementBool( const string &inp, string::size_type &start, string::size_type end, bool def = false )
166 {
167  std::pair< string::size_type, string::size_type >rng = nextElementRange( inp, start, end );
168  return (rng.first
169  == rng.second) ? def : XMLSupport::parse_bool( inp.substr( rng.first,
170  ( (rng.second
171  == string::npos) ? string::npos : (rng.second
172  -rng.first) ) ) );
173 }
174 
175 static string nextElement( string &inp )
176 {
177  string::size_type start = 0;
178  std::pair< string::size_type, string::size_type >rng = nextElementRange( inp, start, string::npos );
179  string ret = inp.substr( rng.first, ( (rng.second == string::npos) ? string::npos : (rng.second-rng.first) ) );
180  inp.erase( 0, ( (rng.second == string::npos) ? string::npos : (rng.second+1) ) );
181  return ret;
182 }
183 
184 static bool stob( const string &inp, bool defaul )
185 {
186  if (inp.length() != 0)
187  return XMLSupport::parse_bool( inp );
188  return defaul;
189 }
190 
191 static double stof( const string &inp, double def = 0 )
192 {
193  if (inp.length() != 0)
194  return XMLSupport::parse_float( inp );
195  return def;
196 }
197 
198 static int stoi( const string &inp, int def = 0 )
199 {
200  if (inp.length() != 0)
201  return XMLSupport::parse_int( inp );
202  return def;
203 }
204 
205 extern bool CheckAccessory( Unit* );
206 
207 extern int parseMountSizes( const char *str );
208 
209 static void AddMounts( Unit *thus, Unit::XML &xml, const std::string &mounts )
210 {
211  string::size_type where, when, ofs = 0;
212  unsigned int first_new_mount = thus->mounts.size();
213  {
214  int nmountz = 0;
215  while ( ( ofs = mounts.find( '{', ofs ) ) != string::npos )
216  nmountz++, ofs++;
217  thus->mounts.reserve( nmountz+thus->mounts.size() );
218  ofs = 0;
219  }
220  while ( ( where = mounts.find( '{', ofs ) ) != string::npos ) {
221  if ( ( when = mounts.find( '}', where+1 ) ) != string::npos ) {
222  string::size_type elemstart = where+1, elemend = when;
223  ofs = when+1;
224 
225  QVector P;
226  QVector Q = QVector( 0, 1, 0 );
227  QVector R = QVector( 0, 0, 1 );
228  QVector pos = QVector( 0, 0, 0 );
229 
230  string filename = nextElementString( mounts, elemstart, elemend );
231  int ammo = nextElementInt( mounts, elemstart, elemend, -1 );
232  int volume = nextElementInt( mounts, elemstart, elemend );
233  string mountsize = nextElementString( mounts, elemstart, elemend );
234  pos.i = nextElementFloat( mounts, elemstart, elemend );
235  pos.j = nextElementFloat( mounts, elemstart, elemend );
236  pos.k = nextElementFloat( mounts, elemstart, elemend );
237  double xyscale = nextElementFloat( mounts, elemstart, elemend );
238  double zscale = nextElementFloat( mounts, elemstart, elemend );
239  R.i = nextElementFloat( mounts, elemstart, elemend );
240  R.j = nextElementFloat( mounts, elemstart, elemend );
241  R.k = nextElementFloat( mounts, elemstart, elemend, 1 );
242  Q.i = nextElementFloat( mounts, elemstart, elemend );
243  Q.j = nextElementFloat( mounts, elemstart, elemend, 1 );
244  Q.k = nextElementFloat( mounts, elemstart, elemend );
245  float func = nextElementFloat( mounts, elemstart, elemend, 1 );
246  float maxfunc = nextElementFloat( mounts, elemstart, elemend, 1 );
247  bool banked = nextElementBool( mounts, elemstart, elemend, false );
248  Q.Normalize();
249  if ( fabs( Q.i ) == fabs( R.i ) && fabs( Q.j ) == fabs( R.j ) && fabs( Q.k ) == fabs( R.k ) ) {
250  Q.i = -1;
251  Q.j = 0;
252  Q.k = 0;
253  }
254  R.Normalize();
255 
256  CrossProduct( Q, R, P );
257  CrossProduct( R, P, Q );
258  Q.Normalize();
259  Mount mnt( filename, ammo, volume, xml.unitscale*xyscale, xml.unitscale*zscale, func, maxfunc, banked );
260  mnt.SetMountOrientation( Quaternion::from_vectors( P.Cast(), Q.Cast(), R.Cast() ) );
261  mnt.SetMountPosition( xml.unitscale*pos.Cast() );
262  int mntsiz = weapon_info::NOWEAP;
263  if ( mountsize.length() ) {
264  mntsiz = parseMountSizes( mountsize.c_str() );
265  mnt.size = mntsiz;
266  } else {
267  mnt.size = mnt.type->size;
268  }
269  thus->mounts.push_back( mnt );
270  } else {ofs = string::npos; }}
271  unsigned char parity = 0;
272  for (unsigned int a = first_new_mount; a < thus->mounts.size(); ++a) {
273  static bool half_sounds = XMLSupport::parse_bool( vs_config->getVariable( "audio", "every_other_mount", "false" ) );
274  if ( (a&1) == parity ) {
275  int b = a;
276  if ( (a&3) == 2 && (int) a < (thus->GetNumMounts()-1) ) {
277  if (thus->mounts[a].type->type != weapon_info::PROJECTILE
278  && thus->mounts[a+1].type->type != weapon_info::PROJECTILE)
279  {
280  b = a+1;
281  }
282  }
283  thus->mounts[b].sound = AUDCreateSound( thus->mounts[b].type->sound, false );
284  } else if ( (!half_sounds) || thus->mounts[a].type->type == weapon_info::PROJECTILE ) {
285  thus->mounts[a].sound = AUDCreateSound( thus->mounts[a].type->sound, false );
286  }
287  if (a > 0)
288  if (thus->mounts[a].sound == thus->mounts[a-1].sound && thus->mounts[a].sound != -1)
289  printf( "Sound error\n" );
290  }
291 }
292 
294 {
295  string filename;
299  double restricted;
300  SubUnitStruct( string fn, QVector POS, QVector q, QVector r, double res )
301  {
302  filename = fn;
303  pos = POS;
304  Q = q;
305  R = r;
306  restricted = res;
307  }
308 };
309 
310 static vector< SubUnitStruct >GetSubUnits( const std::string &subunits )
311 {
312  string::size_type where, when, ofs = 0;
313  vector< SubUnitStruct >ret;
314  {
315  int nelem = 0;
316  while ( ( ofs = subunits.find( '{', ofs ) ) != string::npos )
317  nelem++, ofs++;
318  ret.reserve( nelem );
319  ofs = 0;
320  }
321  while ( ( where = subunits.find( '{', ofs ) ) != string::npos ) {
322  if ( ( when = subunits.find( '}', ofs ) ) != string::npos ) {
323  string::size_type elemstart = where+1, elemend = when;
324  ofs = when+1;
325 
326  QVector pos, Q, R;
327 
328  string filename = nextElementString( subunits, elemstart, elemend );
329  pos.i = nextElementFloat( subunits, elemstart, elemend );
330  pos.j = nextElementFloat( subunits, elemstart, elemend );
331  pos.k = nextElementFloat( subunits, elemstart, elemend );
332  R.i = nextElementFloat( subunits, elemstart, elemend );
333  R.j = nextElementFloat( subunits, elemstart, elemend );
334  R.k = nextElementFloat( subunits, elemstart, elemend );
335  Q.i = nextElementFloat( subunits, elemstart, elemend );
336  Q.j = nextElementFloat( subunits, elemstart, elemend );
337  Q.k = nextElementFloat( subunits, elemstart, elemend );
338  double restricted = cos( nextElementFloat( subunits, elemstart, elemend, 180 )*VS_PI/180.0 );
339 
340  ret.push_back( SubUnitStruct( filename, pos, Q, R, restricted ) );
341  } else {
342  ofs = string::npos;
343  }
344  }
345  return ret;
346 }
347 
348 static void AddSubUnits( Unit *thus, Unit::XML &xml, const std::string &subunits, int faction, const std::string &modification )
349 {
350  if (SERVER || Network) {
351  //Semihack: Keep loading if thus is already a subunit...
352  //A planet can have a wormhole subunit, which itself has more subunits.
353  if (!thus->graphicOptions.SubUnit)
354  return; //subvert all subunits in MP
355  }
356  vector< SubUnitStruct >su = GetSubUnits( subunits );
357  xml.units.reserve( subunits.size()+xml.units.size() );
358  for (vector< SubUnitStruct >::iterator i = su.begin(); i != su.end(); ++i) {
359  string filename = (*i).filename;
360  QVector pos = (*i).pos;
361  QVector Q = (*i).Q;
362  QVector R = (*i).R;
363  double restricted = (*i).restricted;
364  xml.units.push_back( UnitFactory::createUnit( filename.c_str(), true, faction, modification, NULL ) ); //I set here the fg arg to NULL
365  if (xml.units.back()->name == "LOAD_FAILED") {
366  xml.units.back()->limits.yaw = 0;
367  xml.units.back()->limits.pitch = 0;
368  xml.units.back()->limits.roll = 0;
369  xml.units.back()->limits.lateral = xml.units.back()->limits.retro = xml.units.back()->limits.forward =
370  xml.units.back()->limits.afterburn = 0.0;
371  }
372  if ( !thus->isSubUnit() ) //Useless to set recursive owner in subunits - as parent will do the same
373  xml.units.back()->SetRecursiveOwner( thus );
374  xml.units.back()->SetOrientation( Q, R );
375  R.Normalize();
376  xml.units.back()->prev_physical_state = xml.units.back()->curr_physical_state;
377  xml.units.back()->SetPosition( pos*xml.unitscale );
378  xml.units.back()->limits.structurelimits = R.Cast();
379  xml.units.back()->limits.limitmin = restricted;
380  xml.units.back()->name = filename;
381  if (xml.units.back()->pImage->unitwriter != NULL)
382  xml.units.back()->pImage->unitwriter->setName( filename );
383  CheckAccessory( xml.units.back() ); //turns on the ceerazy rotation for the turr
384  }
385  for (int a = xml.units.size()-1; a >= 0; a--) {
386  bool randomspawn = xml.units[a]->name.get().find( "randomspawn" ) != string::npos;
387  if (randomspawn) {
388  int chancetospawn = float_to_int( xml.units[a]->WarpCapData() );
389  if (chancetospawn > rand()%100)
390  thus->SubUnits.prepend( xml.units[a] );
391 
392  else
393  xml.units[a]->Kill();
394  } else {
395  thus->SubUnits.prepend( xml.units[a] );
396  }
397  }
398 }
399 
400 void AddDocks( Unit *thus, Unit::XML &xml, const string &docks )
401 {
402  string::size_type where, when;
403  string::size_type ofs = 0;
404  int overlap = 1;
405  if (SERVER)
406  //NETFIXME: Shouldn't need to know this option on client side, but server references docking port by absolute number.
407  overlap = XMLSupport::parse_int( vs_config->getVariable( "server", "players_per_docking_port", "10" ) );
408  {
409  int nelem = 0;
410  while ( ( ofs = docks.find( '{', ofs ) ) != string::npos )
411  nelem++, ofs++;
412  thus->pImage->dockingports.reserve( nelem*overlap+thus->pImage->dockingports.size() );
413  ofs = 0;
414  }
415  while ( ( where = docks.find( '{', ofs ) ) != string::npos ) {
416  if ( ( when = docks.find( '}', where+1 ) ) != string::npos ) {
417  string::size_type elemstart = where+1, elemend = when;
418  ofs = when+1;
419 
420  QVector pos = QVector( 0, 0, 0 );
421  int type = nextElementInt( docks, elemstart, elemend );
422  pos.i = nextElementFloat( docks, elemstart, elemend );
423  pos.j = nextElementFloat( docks, elemstart, elemend );
424  pos.k = nextElementFloat( docks, elemstart, elemend );
425  double size = nextElementFloat( docks, elemstart, elemend );
426  double minsize = nextElementFloat( docks, elemstart, elemend );
427  for (int i = 0; i < overlap; i++)
428  thus->pImage->dockingports.push_back( DockingPorts( pos.Cast()*xml.unitscale, size*xml.unitscale, minsize
429  *xml.unitscale, DockingPorts::Type::Value(type) ) );
430  } else {
431  ofs = string::npos;
432  }
433  }
434 }
435 
436 void AddLights( Unit *thus, Unit::XML &xml, const string &lights )
437 {
438  static float default_halo_activation =
439  XMLSupport::parse_float( vs_config->getVariable( "graphics", "default_engine_activation", ".00048828125" ) );
440  string::size_type where, when;
441  string::size_type ofs = 0;
442  while ( ( where = lights.find( '{', ofs ) ) != string::npos ) {
443  if ( ( when = lights.find( '}', where+1 ) ) != string::npos ) {
444  string::size_type elemstart = where+1, elemend = when;
445  ofs = when+1;
446 
447  string filename = nextElementString( lights, elemstart, elemend );
448  QVector pos, scale;
449  GFXColor halocolor;
450  pos.i = nextElementFloat( lights, elemstart, elemend );
451  pos.j = nextElementFloat( lights, elemstart, elemend );
452  pos.k = nextElementFloat( lights, elemstart, elemend );
453  scale.i = xml.unitscale*nextElementFloat( lights, elemstart, elemend, 1 );
454  scale.j = scale.k = scale.i;
455  halocolor.r = nextElementFloat( lights, elemstart, elemend, 1 );
456  halocolor.g = nextElementFloat( lights, elemstart, elemend, 1 );
457  halocolor.b = nextElementFloat( lights, elemstart, elemend, 1 );
458  halocolor.a = nextElementFloat( lights, elemstart, elemend, 1 );
459  double act_speed = nextElementFloat( lights, elemstart, elemend, default_halo_activation );
460 
461  thus->addHalo( filename.c_str(), pos*xml.unitscale, scale.Cast(), halocolor, "", act_speed );
462  } else {
463  ofs = string::npos;
464  }
465  }
466 }
467 
468 static void ImportCargo( Unit *thus, const string &imports )
469 {
470  if (Network != NULL)
471  return; //Server takes care of this.
472 
473  string::size_type where, when, ofs = 0;
474  {
475  int nelem = 0;
476  while ( ( ofs = imports.find( '{', ofs ) ) != string::npos )
477  nelem++, ofs++;
478  thus->pImage->cargo.reserve( nelem+thus->pImage->cargo.size() );
479  ofs = 0;
480  }
481  while ( ( where = imports.find( '{', ofs ) ) != string::npos ) {
482  if ( ( when = imports.find( '}', where+1 ) ) != string::npos ) {
483  string::size_type elemstart = where+1, elemend = when;
484  ofs = when+1;
485 
486  string filename = nextElementString( imports, elemstart, elemend );
487  double price = nextElementFloat( imports, elemstart, elemend, 1 );
488  double pricestddev = nextElementFloat( imports, elemstart, elemend );
489  double quant = nextElementFloat( imports, elemstart, elemend, 1 );
490  double quantstddev = nextElementFloat( imports, elemstart, elemend );
491 
492  thus->ImportPartList( filename, price, pricestddev, quant, quantstddev );
493  } else {
494  ofs = string::npos;
495  }
496  }
497 }
498 
499 static void AddCarg( Unit *thus, const string &cargos )
500 {
501  string::size_type where, when, ofs = 0;
502  {
503  int nelem = 0;
504  while ( ( ofs = cargos.find( '{', ofs ) ) != string::npos )
505  nelem++, ofs++;
506  thus->pImage->cargo.reserve( nelem+thus->pImage->cargo.size() );
507  ofs = 0;
508  }
509  while ( ( where = cargos.find( '{', ofs ) ) != string::npos ) {
510  if ( ( when = cargos.find( '}', where+1 ) ) != string::npos ) {
511  Cargo carg;
512  string::size_type elemstart = where+1, elemend = when;
513  ofs = when+1;
514 
515  carg.content = nextElementString( cargos, elemstart, elemend );
516  carg.category = nextElementString( cargos, elemstart, elemend );
517  carg.price = nextElementFloat( cargos, elemstart, elemend );
518  carg.quantity = nextElementInt( cargos, elemstart, elemend );
519  carg.mass = nextElementFloat( cargos, elemstart, elemend );
520  carg.volume = nextElementFloat( cargos, elemstart, elemend );
521  carg.functionality = nextElementFloat( cargos, elemstart, elemend );
522  carg.maxfunctionality = nextElementFloat( cargos, elemstart, elemend );
523  carg.description = nextElementString( cargos, elemstart, elemend );
524  carg.mission = nextElementBool( cargos, elemstart, elemend, false );
525  carg.installed = nextElementBool( cargos, elemstart, elemend,
526  carg.category.get().find("upgrades/") == 0 );
527 
528  thus->AddCargo( carg, false );
529  } else {
530  ofs = string::npos;
531  }
532  }
533 }
534 
535 void HudDamage( float *dam, const string &damages )
536 {
537  if (dam) {
538  string::size_type elemstart = 0, elemend = string::npos;
539  for (int i = 0; i < 1+MAXVDUS+UnitImages< void >::NUMGAUGES; ++i)
540  dam[i] = nextElementFloat( damages, elemstart, elemend, 1 );
541  }
542 }
543 
544 string WriteHudDamage( Unit *un )
545 {
546  string ret;
547  const string semi = ";";
548  if (un->pImage->cockpit_damage) {
549  for (int i = 0; i < 1+MAXVDUS+UnitImages< void >::NUMGAUGES; ++i) {
551  ret += semi;
552  }
553  }
554  return ret;
555 }
556 
558 {
559  string ret;
560  const string semi = ";";
561  if (un->pImage->cockpit_damage) {
563  for (int i = numg; i < 2*numg; ++i) {
565  ret += semi;
566  }
567  }
568  return ret;
569 }
570 
571 void AddSounds( Unit *thus, string sounds )
572 {
573  if (sounds.length() != 0) {
574  string tmp = nextElement( sounds );
575  if ( tmp.length() )
576  thus->sound->shield = AUDCreateSoundWAV( tmp, false );
577  tmp = nextElement( sounds );
578  if ( tmp.length() )
579  thus->sound->armor = AUDCreateSoundWAV( tmp, false );
580  tmp = nextElement( sounds );
581  if ( tmp.length() )
582  thus->sound->hull = AUDCreateSoundWAV( tmp, false );
583  tmp = nextElement( sounds );
584  if ( tmp.length() )
585  thus->sound->jump = AUDCreateSoundWAV( tmp, false );
586  tmp = nextElement( sounds );
587  if ( tmp.length() )
588  thus->sound->explode = AUDCreateSoundWAV( tmp, false );
589  tmp = nextElement( sounds );
590  if ( tmp.length() )
591  thus->sound->cloak = AUDCreateSoundWAV( tmp, false );
592  tmp = nextElement( sounds );
593  if ( tmp.length() )
594  thus->sound->engine = AUDCreateSoundWAV( tmp, true );
595  }
596  if (thus->sound->cloak == -1) {
597  static std::string ssound = vs_config->getVariable( "unitaudio", "cloak", "sfx43.wav" );
598  thus->sound->cloak = AUDCreateSound( ssound, false );
599  }
600  if (thus->sound->engine == -1) {
601  static std::string ssound = vs_config->getVariable( "unitaudio", "afterburner", "sfx10.wav" );
602  thus->sound->engine = AUDCreateSound( ssound, false );
603  }
604  if (thus->sound->shield == -1) {
605  static std::string ssound = vs_config->getVariable( "unitaudio", "shield", "sfx09.wav" );
606  thus->sound->shield = AUDCreateSound( ssound, false );
607  }
608  if (thus->sound->armor == -1) {
609  static std::string ssound = vs_config->getVariable( "unitaudio", "armor", "sfx08.wav" );
610  thus->sound->armor = AUDCreateSound( ssound, false );
611  }
612  if (thus->sound->hull == -1) {
613  static std::string ssound = vs_config->getVariable( "unitaudio", "armor", "sfx08.wav" );
614  thus->sound->hull = AUDCreateSound( ssound, false );
615  }
616  if (thus->sound->explode == -1) {
617  static std::string ssound = vs_config->getVariable( "unitaudio", "explode", "explosion.wav" );
618  thus->sound->explode = AUDCreateSound( ssound, false );
619  }
620  if (thus->sound->jump == -1) {
621  static std::string ssound = vs_config->getVariable( "unitaudio", "explode", "sfx43.wav" );
622  thus->sound->jump = AUDCreateSound( ssound, false );
623  }
624 }
625 
626 void LoadCockpit( Unit *thus, const string &cockpit )
627 {
628  string::size_type elemstart = 0, elemend = string::npos;
629  thus->pImage->cockpitImage = nextElementString( cockpit, elemstart, elemend );
630  thus->pImage->CockpitCenter.i = nextElementFloat( cockpit, elemstart, elemend );
631  thus->pImage->CockpitCenter.j = nextElementFloat( cockpit, elemstart, elemend );
632  thus->pImage->CockpitCenter.k = nextElementFloat( cockpit, elemstart, elemend );
633 }
634 
635 static int AssignIf( const string &inp, float &val, float &val1, float &val2 )
636 {
637  if ( inp.length() ) {
638  val = ::stof( inp );
639  val1 = ::stof( inp );
640  val2 = ::stof( inp );
641  return 1;
642  }
643  return 0;
644 }
645 
646 static int AssignIfDeg( const string &inp, float &val )
647 {
648  if ( inp.length() ) {
649  val = ::stof( inp )*VS_PI/180;
650  return 1;
651  }
652  return 0;
653 }
654 
656 {
657  static float fuel_conversion = XMLSupport::parse_float( vs_config->getVariable( "physics", "FuelConversion", ".00144" ) );
658  return fuel_conversion;
659 }
660 
661 const std::string EMPTY_STRING( "" );
662 
663 #define LOADROW_OPTIMIZER ((0x348299ab))
664 
665  /*After all, it's always used in the end*/
666 #define FORCE_OPTIMIZER (1)
667 
668 #define OPTIMIZER_INDEX( Variable ) OPTIDX_##Variable
669 
670 #define INIT_OPTIMIZER( keys, Variable ) do{OPTIMIZER_INDEX(Variable)=(keys.push_back(#Variable),(keys.size()-1));}while(0)
671 
672 #define DEF_OPTIMIZER( Variable ) static unsigned int OPTIMIZER_INDEX( Variable ) = CSVTable::optimizer_undefined;
673 
674 #if FORCE_OPTIMIZER
675 
676 #define OPTIM_GET( row, table, variable ) \
677  (( \
678  (table->optimizer_indexes[OPTIMIZER_INDEX(variable)] == CSVTable::optimizer_undefined) \
679  ? EMPTY_STRING \
680  : row[ table->optimizer_indexes[OPTIMIZER_INDEX(variable)] ] \
681  ))
682 
683 #else
684 
685 #define OPTIM_GET( row, table, variable ) \
686  (( \
687  use_optimizer \
688  ? ( \
689  ( \
690  (OPTIMIZER_INDEX(variable) == CSVTable::optimizer_undefined) \
691  || ( \
692  table->optimizer_indexes[OPTIMIZER_INDEX(variable)] \
693  == CSVTable::optimizer_undefined \
694  ) \
695  ) \
696  ? EMPTY_STRING \
697  : row[table->optimizer_indexes[OPTIMIZER_INDEX(variable )]] \
698  ) \
699  : row[#variable] \
700  ))
701 
702 #endif
703 
704 void Unit::LoadRow( CSVRow &row, string modification, string *netxml )
705 {
706  CSVTable *table = row.getParent();
707  Unit::XML xml;
708  xml.unitModifications = modification.c_str();
709  xml.randomstartframe = ( (float) rand() )/RAND_MAX;
710  xml.randomstartseconds = 0;
711  xml.calculated_role = false;
712  xml.damageiterator = 0;
713  xml.shieldmesh = NULL;
714  xml.rapidmesh = NULL;
715  xml.hasColTree = true;
716  xml.unitlevel = 0;
717  xml.unitscale = 1;
718  xml.data = xml.shieldmesh = xml.rapidmesh = NULL; //was uninitialized memory
719  string tmpstr;
720  csvRow = row[0];
721  DEF_OPTIMIZER( FaceCamera );
722  DEF_OPTIMIZER( Name );
723  DEF_OPTIMIZER( Hud_image );
724  DEF_OPTIMIZER( Combat_Role ); //legacy only
725  DEF_OPTIMIZER( Unit_Role );
726  DEF_OPTIMIZER( Attack_Preference );
727  DEF_OPTIMIZER( Num_Animation_Stages );
728  DEF_OPTIMIZER( Unit_Scale );
729  DEF_OPTIMIZER( Mesh );
730  DEF_OPTIMIZER( Dock );
731  DEF_OPTIMIZER( Sub_Units );
732  DEF_OPTIMIZER( Mounts );
733  DEF_OPTIMIZER( Hold_Volume );
734  DEF_OPTIMIZER( Hidden_Hold_Volume );
735  DEF_OPTIMIZER( Upgrade_Storage_Volume );
736  DEF_OPTIMIZER( Equipment_Space );
737  DEF_OPTIMIZER( Cargo_Import );
738  DEF_OPTIMIZER( Cargo );
739  DEF_OPTIMIZER( Sounds );
741  DEF_OPTIMIZER( CockpitX );
742  DEF_OPTIMIZER( CockpitY );
743  DEF_OPTIMIZER( CockpitZ );
744  DEF_OPTIMIZER( Mass );
745  DEF_OPTIMIZER( Moment_Of_Inertia );
746  DEF_OPTIMIZER( Fuel_Capacity );
747  DEF_OPTIMIZER( Hull );
748  DEF_OPTIMIZER( Armor_Front_Top_Left );
749  DEF_OPTIMIZER( Armor_Front_Top_Right );
750  DEF_OPTIMIZER( Armor_Back_Top_Left );
751  DEF_OPTIMIZER( Armor_Back_Top_Right );
752  DEF_OPTIMIZER( Armor_Front_Bottom_Left );
753  DEF_OPTIMIZER( Armor_Front_Bottom_Right );
754  DEF_OPTIMIZER( Armor_Back_Bottom_Left );
755  DEF_OPTIMIZER( Armor_Back_Bottom_Right );
756  DEF_OPTIMIZER( Description );
757  DEF_OPTIMIZER( Shield_Front_Top_Left );
758  DEF_OPTIMIZER( Shield_Front_Top_Right );
759  DEF_OPTIMIZER( Shield_Back_Top_Left );
760  DEF_OPTIMIZER( Shield_Back_Top_Right );
761  DEF_OPTIMIZER( Shield_Front_Bottom_Left );
762  DEF_OPTIMIZER( Shield_Front_Bottom_Right );
763  DEF_OPTIMIZER( Shield_Back_Bottom_Left );
764  DEF_OPTIMIZER( Shield_Back_Bottom_Right );
765  DEF_OPTIMIZER( Shield_Leak );
766  DEF_OPTIMIZER( Shield_Recharge );
767  DEF_OPTIMIZER( Shield_Efficiency );
768  DEF_OPTIMIZER( Warp_Capacitor );
769  DEF_OPTIMIZER( Warp_Min_Multiplier );
770  DEF_OPTIMIZER( Warp_Max_Multiplier );
771  DEF_OPTIMIZER( Primary_Capacitor );
772  DEF_OPTIMIZER( Reactor_Recharge );
773  DEF_OPTIMIZER( Jump_Drive_Present );
774  DEF_OPTIMIZER( Jump_Drive_Delay );
775  DEF_OPTIMIZER( Wormhole );
776  DEF_OPTIMIZER( Collide_Subunits );
777  DEF_OPTIMIZER( Outsystem_Jump_Cost );
778  DEF_OPTIMIZER( Warp_Usage_Cost );
779  DEF_OPTIMIZER( Afterburner_Usage_Cost );
780  DEF_OPTIMIZER( Afterburner_Type );
781  DEF_OPTIMIZER( Maneuver_Yaw );
782  DEF_OPTIMIZER( Maneuver_Pitch );
783  DEF_OPTIMIZER( Maneuver_Roll );
784  DEF_OPTIMIZER( Yaw_Governor );
785  DEF_OPTIMIZER( Yaw_Governor_Right );
786  DEF_OPTIMIZER( Yaw_Governor_Left );
787  DEF_OPTIMIZER( Pitch_Governor );
788  DEF_OPTIMIZER( Pitch_Governor_Up );
789  DEF_OPTIMIZER( Pitch_Governor_Down );
790  DEF_OPTIMIZER( Roll_Governor );
791  DEF_OPTIMIZER( Roll_Governor_Right );
792  DEF_OPTIMIZER( Roll_Governor_Left );
793  DEF_OPTIMIZER( Afterburner_Accel );
794  DEF_OPTIMIZER( Forward_Accel );
795  DEF_OPTIMIZER( Retro_Accel );
796  DEF_OPTIMIZER( Left_Accel );
797  DEF_OPTIMIZER( Right_Accel );
798  DEF_OPTIMIZER( Top_Accel );
799  DEF_OPTIMIZER( Bottom_Accel );
800  DEF_OPTIMIZER( Default_Speed_Governor );
801  DEF_OPTIMIZER( Afterburner_Speed_Governor );
802  DEF_OPTIMIZER( ITTS );
803  DEF_OPTIMIZER( Can_Lock );
804  DEF_OPTIMIZER( Radar_Color );
805  DEF_OPTIMIZER( Radar_Range );
806  DEF_OPTIMIZER( Max_Cone );
807  DEF_OPTIMIZER( Tracking_Cone );
808  DEF_OPTIMIZER( Lock_Cone );
809  DEF_OPTIMIZER( Cloak_Min );
810  DEF_OPTIMIZER( Cloak_Glass );
811  DEF_OPTIMIZER( Can_Cloak );
812  DEF_OPTIMIZER( Cloak_Rate );
813  DEF_OPTIMIZER( Cloak_Energy );
814  DEF_OPTIMIZER( Repair_Droid );
815  DEF_OPTIMIZER( ECM_Rating );
816  DEF_OPTIMIZER( Heat_Sink_Rating );
817  DEF_OPTIMIZER( Hud_Functionality );
818  DEF_OPTIMIZER( Max_Hud_Functionality );
819  DEF_OPTIMIZER( Lifesupport_Functionality );
820  DEF_OPTIMIZER( Max_Lifesupport_Functionality );
821  DEF_OPTIMIZER( Comm_Functionality );
822  DEF_OPTIMIZER( Max_Comm_Functionality );
823  DEF_OPTIMIZER( FireControl_Functionality );
824  DEF_OPTIMIZER( Max_FireControl_Functionality );
825  DEF_OPTIMIZER( SPECDrive_Functionality );
826  DEF_OPTIMIZER( Max_SPECDrive_Functionality );
827  DEF_OPTIMIZER( Slide_Start );
828  DEF_OPTIMIZER( Slide_End );
829  DEF_OPTIMIZER( Upgrades );
830  DEF_OPTIMIZER( Tractorability );
831  DEF_OPTIMIZER( Explosion );
832  DEF_OPTIMIZER( Light );
833  DEF_OPTIMIZER( Shield_Mesh );
834  DEF_OPTIMIZER( Rapid_Mesh );
835  DEF_OPTIMIZER( Use_Rapid );
836  DEF_OPTIMIZER( NoDamageParticles );
837  DEF_OPTIMIZER( Spec_Interdiction );
838  if (table && !table->optimizer_setup) {
839  static std::vector< std::string >keys;
840  static bool optimizer_keys_init = false;
841  if (!optimizer_keys_init) {
842  optimizer_keys_init = true;
843  printf( "Initializing optimizer\n" );
844  INIT_OPTIMIZER( keys, Name );
845  INIT_OPTIMIZER( keys, Hud_image );
846  INIT_OPTIMIZER( keys, FaceCamera );
847  INIT_OPTIMIZER( keys, Combat_Role ); //legacy only
848  INIT_OPTIMIZER( keys, Unit_Role );
849  INIT_OPTIMIZER( keys, Attack_Preference );
850  INIT_OPTIMIZER( keys, Num_Animation_Stages );
851  INIT_OPTIMIZER( keys, Unit_Scale );
852  INIT_OPTIMIZER( keys, Mesh );
853  INIT_OPTIMIZER( keys, Dock );
854  INIT_OPTIMIZER( keys, Sub_Units );
855  INIT_OPTIMIZER( keys, Mounts );
856  INIT_OPTIMIZER( keys, Hold_Volume );
857  INIT_OPTIMIZER( keys, Hidden_Hold_Volume );
858  INIT_OPTIMIZER( keys, Upgrade_Storage_Volume );
859  INIT_OPTIMIZER( keys, Equipment_Space );
860  INIT_OPTIMIZER( keys, Cargo_Import );
861  INIT_OPTIMIZER( keys, Cargo );
862  INIT_OPTIMIZER( keys, Sounds );
863  INIT_OPTIMIZER( keys, Cockpit );
864  INIT_OPTIMIZER( keys, CockpitX );
865  INIT_OPTIMIZER( keys, CockpitY );
866  INIT_OPTIMIZER( keys, CockpitZ );
867  INIT_OPTIMIZER( keys, Mass );
868  INIT_OPTIMIZER( keys, Moment_Of_Inertia );
869  INIT_OPTIMIZER( keys, Fuel_Capacity );
870  INIT_OPTIMIZER( keys, Hull );
871  INIT_OPTIMIZER( keys, Armor_Front_Top_Left );
872  INIT_OPTIMIZER( keys, Armor_Front_Top_Right );
873  INIT_OPTIMIZER( keys, Armor_Back_Top_Left );
874  INIT_OPTIMIZER( keys, Armor_Back_Top_Right );
875  INIT_OPTIMIZER( keys, Armor_Front_Bottom_Left );
876  INIT_OPTIMIZER( keys, Armor_Front_Bottom_Right );
877  INIT_OPTIMIZER( keys, Armor_Back_Bottom_Left );
878  INIT_OPTIMIZER( keys, Armor_Back_Bottom_Right );
879  INIT_OPTIMIZER( keys, Description );
880  INIT_OPTIMIZER( keys, Shield_Front_Top_Left );
881  INIT_OPTIMIZER( keys, Shield_Front_Top_Right );
882  INIT_OPTIMIZER( keys, Shield_Back_Top_Left );
883  INIT_OPTIMIZER( keys, Shield_Back_Top_Right );
884  INIT_OPTIMIZER( keys, Shield_Front_Bottom_Left );
885  INIT_OPTIMIZER( keys, Shield_Front_Bottom_Right );
886  INIT_OPTIMIZER( keys, Shield_Back_Bottom_Left );
887  INIT_OPTIMIZER( keys, Shield_Back_Bottom_Right );
888  INIT_OPTIMIZER( keys, Shield_Leak );
889  INIT_OPTIMIZER( keys, Shield_Recharge );
890  INIT_OPTIMIZER( keys, Shield_Efficiency );
891  INIT_OPTIMIZER( keys, Warp_Capacitor );
892  INIT_OPTIMIZER( keys, Warp_Min_Multiplier );
893  INIT_OPTIMIZER( keys, Warp_Max_Multiplier );
894  INIT_OPTIMIZER( keys, Primary_Capacitor );
895  INIT_OPTIMIZER( keys, Reactor_Recharge );
896  INIT_OPTIMIZER( keys, Jump_Drive_Present );
897  INIT_OPTIMIZER( keys, Jump_Drive_Delay );
898  INIT_OPTIMIZER( keys, Wormhole );
899  INIT_OPTIMIZER( keys, Collide_Subunits );
900  INIT_OPTIMIZER( keys, Outsystem_Jump_Cost );
901  INIT_OPTIMIZER( keys, Warp_Usage_Cost );
902  INIT_OPTIMIZER( keys, Afterburner_Usage_Cost );
903  INIT_OPTIMIZER( keys, Afterburner_Type );
904  INIT_OPTIMIZER( keys, Maneuver_Yaw );
905  INIT_OPTIMIZER( keys, Maneuver_Pitch );
906  INIT_OPTIMIZER( keys, Maneuver_Roll );
907  INIT_OPTIMIZER( keys, Yaw_Governor );
908  INIT_OPTIMIZER( keys, Yaw_Governor_Right );
909  INIT_OPTIMIZER( keys, Yaw_Governor_Left );
910  INIT_OPTIMIZER( keys, Pitch_Governor );
911  INIT_OPTIMIZER( keys, Pitch_Governor_Up );
912  INIT_OPTIMIZER( keys, Pitch_Governor_Down );
913  INIT_OPTIMIZER( keys, Roll_Governor );
914  INIT_OPTIMIZER( keys, Roll_Governor_Right );
915  INIT_OPTIMIZER( keys, Roll_Governor_Left );
916  INIT_OPTIMIZER( keys, Afterburner_Accel );
917  INIT_OPTIMIZER( keys, Forward_Accel );
918  INIT_OPTIMIZER( keys, Retro_Accel );
919  INIT_OPTIMIZER( keys, Left_Accel );
920  INIT_OPTIMIZER( keys, Right_Accel );
921  INIT_OPTIMIZER( keys, Top_Accel );
922  INIT_OPTIMIZER( keys, Bottom_Accel );
923  INIT_OPTIMIZER( keys, Default_Speed_Governor );
924  INIT_OPTIMIZER( keys, Afterburner_Speed_Governor );
925  INIT_OPTIMIZER( keys, ITTS );
926  INIT_OPTIMIZER( keys, Can_Lock );
927  INIT_OPTIMIZER( keys, Radar_Color );
928  INIT_OPTIMIZER( keys, Radar_Range );
929  INIT_OPTIMIZER( keys, Max_Cone );
930  INIT_OPTIMIZER( keys, Tracking_Cone );
931  INIT_OPTIMIZER( keys, Lock_Cone );
932  INIT_OPTIMIZER( keys, Cloak_Min );
933  INIT_OPTIMIZER( keys, Cloak_Glass );
934  INIT_OPTIMIZER( keys, Can_Cloak );
935  INIT_OPTIMIZER( keys, Cloak_Rate );
936  INIT_OPTIMIZER( keys, Cloak_Energy );
937  INIT_OPTIMIZER( keys, Repair_Droid );
938  INIT_OPTIMIZER( keys, ECM_Rating );
939  INIT_OPTIMIZER( keys, Heat_Sink_Rating );
940  INIT_OPTIMIZER( keys, Hud_Functionality );
941  INIT_OPTIMIZER( keys, Max_Hud_Functionality );
942  INIT_OPTIMIZER( keys, Lifesupport_Functionality );
943  INIT_OPTIMIZER( keys, Max_Lifesupport_Functionality );
944  INIT_OPTIMIZER( keys, Comm_Functionality );
945  INIT_OPTIMIZER( keys, Max_Comm_Functionality );
946  INIT_OPTIMIZER( keys, FireControl_Functionality );
947  INIT_OPTIMIZER( keys, Max_FireControl_Functionality );
948  INIT_OPTIMIZER( keys, SPECDrive_Functionality );
949  INIT_OPTIMIZER( keys, Max_SPECDrive_Functionality );
950  INIT_OPTIMIZER( keys, Slide_Start );
951  INIT_OPTIMIZER( keys, Slide_End );
952  INIT_OPTIMIZER( keys, Upgrades );
953  INIT_OPTIMIZER( keys, Tractorability );
954  INIT_OPTIMIZER( keys, Explosion );
955  INIT_OPTIMIZER( keys, Light );
956  INIT_OPTIMIZER( keys, Shield_Mesh );
957  INIT_OPTIMIZER( keys, Rapid_Mesh );
958  INIT_OPTIMIZER( keys, Use_Rapid );
959  INIT_OPTIMIZER( keys, NoDamageParticles );
960  INIT_OPTIMIZER( keys, Spec_Interdiction );
961  }
962  table->SetupOptimizer( keys, LOADROW_OPTIMIZER );
963  }
964  //begin the geometry (and things that depend on stats)
965  fullname = OPTIM_GET( row, table, Name );
966  if ( ( tmpstr = OPTIM_GET( row, table, Hud_image ) ).length() != 0 ) {
967  std::string fac = FactionUtil::GetFaction( faction );
968  fac += "_";
969  fac += tmpstr;
970  pImage->pHudImage = createVSSprite( fac.c_str() );
971  if ( !isVSSpriteLoaded( pImage->pHudImage ) ) {
973  pImage->pHudImage = createVSSprite( tmpstr.c_str() );
974  }
975  }
976  if ( ( tmpstr = OPTIM_GET( row, table, FaceCamera ) ).length() != 0 )
978  std::string llegacy_combat_role( OPTIM_GET( row, table, Combat_Role ) );
979  std::string lunit_role( OPTIM_GET( row, table, Unit_Role ) );
980  std::string lattack_preference( OPTIM_GET( row, table, Attack_Preference ) );
981  if (lunit_role.length() == 0)
982  this->setUnitRole( llegacy_combat_role );
983  else
984  this->setUnitRole( lunit_role );
985  if (lattack_preference.length() == 0)
986  this->setAttackPreference( llegacy_combat_role );
987  else
988  this->setAttackPreference( lattack_preference );
989  graphicOptions.NumAnimationPoints = stoi( OPTIM_GET( row, table, Num_Animation_Stages ), 0 );
990  graphicOptions.NoDamageParticles = stoi( OPTIM_GET( row, table, NoDamageParticles ), 0 );
993  xml.unitscale = stof( OPTIM_GET( row, table, Unit_Scale ), 1 );
994  if (!xml.unitscale) xml.unitscale = 1;
995  pImage->unitscale = xml.unitscale;
997  table,
998  Mesh ), faction,
999  getFlightgroup() );
1000  AddDocks( this, xml, OPTIM_GET( row, table, Dock ) );
1001  AddSubUnits( this, xml, OPTIM_GET( row, table, Sub_Units ), faction, modification );
1002 
1003  meshdata = xml.meshes;
1004  meshdata.push_back( NULL );
1005  corner_min = Vector( FLT_MAX, FLT_MAX, FLT_MAX );
1006  corner_max = Vector( -FLT_MAX, -FLT_MAX, -FLT_MAX );
1007  calculate_extent( false );
1008  AddMounts( this, xml, OPTIM_GET( row, table, Mounts ) );
1009  this->pImage->CargoVolume = ::stof( OPTIM_GET( row, table, Hold_Volume ) );
1010  this->pImage->HiddenCargoVolume = ::stof( OPTIM_GET( row, table, Hidden_Hold_Volume ) );
1011  this->pImage->UpgradeVolume = ::stof( OPTIM_GET( row, table, Upgrade_Storage_Volume ) );
1012  this->pImage->equipment_volume = ::stof( OPTIM_GET( row, table, Equipment_Space ) );
1013  ImportCargo( this, OPTIM_GET( row, table, Cargo_Import ) ); //if this changes change planet_generic.cpp
1014  AddCarg( this, OPTIM_GET( row, table, Cargo ) );
1015  AddSounds( this, OPTIM_GET( row, table, Sounds ) );
1016  LoadCockpit( this, OPTIM_GET( row, table, Cockpit ) );
1017  pImage->CockpitCenter.i = ::stof( OPTIM_GET( row, table, CockpitX ) )*xml.unitscale;
1018  pImage->CockpitCenter.j = ::stof( OPTIM_GET( row, table, CockpitY ) )*xml.unitscale;
1019  pImage->CockpitCenter.k = ::stof( OPTIM_GET( row, table, CockpitZ ) )*xml.unitscale;
1020  Mass = stof( OPTIM_GET( row, table, Mass ), 1.0 );
1021  Momentofinertia = stof( OPTIM_GET( row, table, Moment_Of_Inertia ), 1.0 );
1022  fuel = ::stof( OPTIM_GET( row, table, Fuel_Capacity ) );
1023  hull = maxhull = ::stof( OPTIM_GET( row, table, Hull ) );
1024  specInterdiction = ::stof( OPTIM_GET( row, table, Spec_Interdiction ) );
1025  armor.frontlefttop = ::stof( OPTIM_GET( row, table, Armor_Front_Top_Left ) );
1026  armor.frontrighttop = ::stof( OPTIM_GET( row, table, Armor_Front_Top_Right ) );
1027  armor.backlefttop = ::stof( OPTIM_GET( row, table, Armor_Back_Top_Left ) );
1028  armor.backrighttop = ::stof( OPTIM_GET( row, table, Armor_Back_Top_Right ) );
1029  armor.frontleftbottom = ::stof( OPTIM_GET( row, table, Armor_Front_Bottom_Left ) );
1030  armor.frontrightbottom = ::stof( OPTIM_GET( row, table, Armor_Front_Bottom_Right ) );
1031  armor.backleftbottom = ::stof( OPTIM_GET( row, table, Armor_Back_Bottom_Left ) );
1032  armor.backrightbottom = ::stof( OPTIM_GET( row, table, Armor_Back_Bottom_Right ) );
1033  int shieldcount = 0;
1034  Shield two;
1035  Shield four;
1036  Shield eight;
1037  memset( &two, 0, sizeof (Shield) );
1038  memset( &four, 0, sizeof (Shield) );
1039  memset( &eight, 0, sizeof (Shield) );
1040  shieldcount += AssignIf( OPTIM_GET( row, table, Shield_Front_Top_Right ),
1041  two.shield2fb.front, four.shield4fbrl.front, eight.shield8.frontrighttop );
1042  shieldcount += AssignIf( OPTIM_GET( row, table, Shield_Front_Top_Left ),
1043  two.shield2fb.front, four.shield4fbrl.front, eight.shield8.frontlefttop );
1044  shieldcount += AssignIf( OPTIM_GET( row, table, Shield_Back_Top_Left ),
1045  two.shield2fb.back, four.shield4fbrl.back, eight.shield8.backlefttop );
1046  shieldcount += AssignIf( OPTIM_GET( row, table, Shield_Back_Top_Right ),
1047  two.shield2fb.back, four.shield4fbrl.back, eight.shield8.backrighttop );
1048  shieldcount += AssignIf( OPTIM_GET( row, table, Shield_Front_Bottom_Left ),
1049  two.shield2fb.front, four.shield4fbrl.left, eight.shield8.frontleftbottom );
1050  shieldcount += AssignIf( OPTIM_GET( row, table, Shield_Front_Bottom_Right ),
1051  two.shield2fb.front, four.shield4fbrl.right, eight.shield8.frontrightbottom );
1052  shieldcount += AssignIf( OPTIM_GET( row, table, Shield_Back_Bottom_Left ),
1053  two.shield2fb.back, four.shield4fbrl.left, eight.shield8.backleftbottom );
1054  shieldcount += AssignIf( OPTIM_GET( row, table, Shield_Back_Bottom_Right ),
1055  two.shield2fb.back, four.shield4fbrl.right, eight.shield8.backrightbottom );
1056  two.shield2fb.frontmax = two.shield2fb.front;
1057  two.shield2fb.backmax = two.shield2fb.back;
1058  four.shield4fbrl.frontmax = four.shield4fbrl.front;
1059  four.shield4fbrl.backmax = four.shield4fbrl.back;
1060  four.shield4fbrl.rightmax = four.shield4fbrl.right;
1061  four.shield4fbrl.leftmax = four.shield4fbrl.left;
1062  eight.shield8.frontlefttopmax = eight.shield8.frontlefttop;
1063  eight.shield8.frontrighttopmax = eight.shield8.frontrighttop;
1064  eight.shield8.backrighttopmax = eight.shield8.backrighttop;
1065  eight.shield8.backlefttopmax = eight.shield8.backlefttop;
1066  eight.shield8.frontleftbottommax = eight.shield8.frontleftbottom;
1067  eight.shield8.frontrightbottommax = eight.shield8.frontrightbottom;
1068  eight.shield8.backrightbottommax = eight.shield8.backrightbottom;
1069  eight.shield8.backleftbottommax = eight.shield8.backleftbottom;
1070  float r45 = VS_PI/4;
1071  float r90 = VS_PI/2;
1072  float r135 = 3*VS_PI/4;
1073  float r180 = VS_PI;
1074  float r225 = 5*VS_PI/4;
1075  float r270 = 3*VS_PI/2;
1076  float r315 = 7*VS_PI/4;
1077  float r360 = 2*VS_PI;
1078  int iter;
1079  if (shieldcount > MAX_SHIELD_NUMBER)
1080  shieldcount = MAX_SHIELD_NUMBER;
1081  memset( shield.range, 0, sizeof (shield.range) );
1082  if (shieldcount == 8) {
1083  shield.number = 8;
1084  shield.shield.cur[0] = shield.shield.max[0] = eight.shield8.frontlefttopmax;
1085  shield.range[0].thetamin = 0;
1086  shield.range[0].thetamax = r90;
1087  shield.range[0].rhomin = 0;
1088  shield.range[0].rhomax = r90;
1089 
1090  shield.shield.cur[1] = shield.shield.max[1] = eight.shield8.backlefttopmax;
1091  shield.range[1].thetamin = r90;
1092  shield.range[1].thetamax = r180;
1093  shield.range[1].rhomin = 0;
1094  shield.range[1].rhomax = r90;
1095 
1096  shield.shield.cur[2] = shield.shield.max[2] = eight.shield8.frontrighttopmax;
1097  shield.range[2].thetamin = r270;
1098  shield.range[2].thetamax = r360;
1099  shield.range[2].rhomin = 0;
1100  shield.range[2].rhomax = r90;
1101 
1102  shield.shield.cur[3] = shield.shield.max[3] = eight.shield8.backrighttopmax;
1103  shield.range[3].thetamin = r180;
1104  shield.range[3].thetamax = r270;
1105  shield.range[3].rhomin = 0;
1106  shield.range[3].rhomax = r90;
1107 
1108  shield.shield.cur[4] = shield.shield.max[4] = eight.shield8.frontleftbottommax;
1109  shield.range[4].thetamin = 0;
1110  shield.range[4].thetamax = r90;
1111  shield.range[4].rhomin = -r90;
1112  shield.range[4].rhomax = 0;
1113 
1114  shield.shield.cur[5] = shield.shield.max[5] = eight.shield8.backleftbottommax;
1115  shield.range[5].thetamin = r90;
1116  shield.range[5].thetamax = r180;
1117  shield.range[5].rhomin = -r90;
1118  shield.range[5].rhomax = 0;
1119 
1120  shield.shield.cur[6] = shield.shield.max[6] = eight.shield8.frontrightbottommax;
1121  shield.range[6].thetamin = r270;
1122  shield.range[6].thetamax = r360;
1123  shield.range[6].rhomin = -r90;
1124  shield.range[6].rhomax = 0;
1125 
1126  shield.shield.cur[7] = shield.shield.max[7] = eight.shield8.backrightbottommax;
1127  shield.range[7].thetamin = r180;
1128  shield.range[7].thetamax = r270;
1129  shield.range[7].rhomin = -r90;
1130  shield.range[7].rhomax = 0;
1131  } else if (shieldcount == 4) {
1132  shield.number = 4;
1133 
1134  shield.shield.cur[0] = shield.shield.max[0] = four.shield4fbrl.frontmax;
1135  shield.range[0].thetamin = r315;
1136  shield.range[0].thetamax = r360+r45;
1137  shield.range[0].rhomin = -r90;
1138  shield.range[0].rhomax = r90;
1139 
1140  shield.shield.cur[1] = shield.shield.max[1] = four.shield4fbrl.backmax;
1141  shield.range[1].thetamin = r135;
1142  shield.range[1].thetamax = r225;
1143  shield.range[1].rhomin = -r90;
1144  shield.range[1].rhomax = r90;
1145 
1146  shield.shield.cur[2] = shield.shield.max[2] = four.shield4fbrl.rightmax;
1147  shield.range[2].thetamin = r225;
1148  shield.range[2].thetamax = r315;
1149  shield.range[2].rhomin = -r90;
1150  shield.range[2].rhomax = r90;
1151 
1152  shield.shield.cur[3] = shield.shield.max[3] = four.shield4fbrl.leftmax;
1153  shield.range[3].thetamin = r45;
1154  shield.range[3].thetamax = r225;
1155  shield.range[3].rhomin = -r90;
1156  shield.range[3].rhomax = r90;
1157  } else if (shieldcount == 2) {
1158  shield.number = 2;
1159 
1160  shield.shield.cur[0] = shield.shield.max[0] = four.shield2fb.frontmax;
1161  shield.range[0].thetamin = r270;
1162  shield.range[0].thetamax = r360+r90;
1163  shield.range[0].rhomin = -r90;
1164  shield.range[0].rhomax = r90;
1165 
1166  shield.shield.cur[1] = shield.shield.max[1] = four.shield2fb.backmax;
1167  shield.range[1].thetamin = r90;
1168  shield.range[1].thetamax = r270;
1169  shield.range[1].rhomin = -r90;
1170  shield.range[1].rhomax = r90;
1171  } else {
1172  //No shields
1173  shield.number = 0;
1174  }
1175  for (iter = 0; iter < shieldcount; ++iter) {
1176  std::string shieldname = "Shield_"+XMLSupport::tostring( iter );
1177  AssignIfDeg( row[shieldname+"_Min_Theta"], shield.range[iter].thetamin );
1178  AssignIfDeg( row[shieldname+"_Max_Theta"], shield.range[iter].thetamax );
1179  AssignIfDeg( row[shieldname+"_Min_Rho"], shield.range[iter].rhomin );
1180  AssignIfDeg( row[shieldname+"_Max_Rho"], shield.range[iter].rhomax );
1181  }
1182  shield.leak = (char) (::stof( OPTIM_GET( row, table, Shield_Leak ) )*100.0);
1183  shield.recharge = ::stof( OPTIM_GET( row, table, Shield_Recharge ) );
1184  shield.efficiency = ::stof( OPTIM_GET( row, table, Shield_Efficiency ), 1.0 );
1185 
1186  static bool WCfuelhack = XMLSupport::parse_bool( vs_config->getVariable( "physics", "fuel_equals_warp", "false" ) );
1187  maxwarpenergy = warpenergy = ::stof( OPTIM_GET( row, table, Warp_Capacitor ) );
1188 
1189  graphicOptions.MinWarpMultiplier = ::stof( OPTIM_GET( row, table, Warp_Min_Multiplier ), 1.0 );
1190  graphicOptions.MaxWarpMultiplier = ::stof( OPTIM_GET( row, table, Warp_Max_Multiplier ), 1.0 );
1191 
1192  maxenergy = energy = ::stof( OPTIM_GET( row, table, Primary_Capacitor ) );
1193  recharge = ::stof( OPTIM_GET( row, table, Reactor_Recharge ) );
1194  jump.drive = XMLSupport::parse_bool( OPTIM_GET( row, table, Jump_Drive_Present ) ) ? -1 : -2;
1195  jump.delay = ::stoi( OPTIM_GET( row, table, Jump_Drive_Delay ) );
1196  pImage->forcejump = XMLSupport::parse_bool( OPTIM_GET( row, table, Wormhole ) );
1198  table,
1199  Collide_Subunits ),
1200  graphicOptions.RecurseIntoSubUnitsOnCollision ? true : false ) ? 1
1201  : 0;
1202  jump.energy = ::stof( OPTIM_GET( row, table, Outsystem_Jump_Cost ) );
1203  jump.insysenergy = ::stof( OPTIM_GET( row, table, Warp_Usage_Cost ) );
1204  if (WCfuelhack) fuel = warpenergy = warpenergy+jump.energy*0.1f; //this is required to make sure we don't trigger the "globally out of fuel" if we use all warp charges -- save some afterburner for later!!!
1205  afterburnenergy = ::stof( OPTIM_GET( row, table, Afterburner_Usage_Cost ), 32767 );
1206  afterburntype = ::stoi( OPTIM_GET( row, table, Afterburner_Type ) ); //type 1 == "use fuel", type 0 == "use reactor energy", type 2 ==(hopefully) "use jump fuel" 3: NO AFTERBURNER
1207  limits.yaw = ::stof( OPTIM_GET( row, table, Maneuver_Yaw ) )*VS_PI/180.;
1208  limits.pitch = ::stof( OPTIM_GET( row, table, Maneuver_Pitch ) )*VS_PI/180.;
1209  limits.roll = ::stof( OPTIM_GET( row, table, Maneuver_Roll ) )*VS_PI/180.;
1210  {
1211  std::string t, tn, tp;
1212  t = OPTIM_GET( row, table, Yaw_Governor );
1213  tn = OPTIM_GET( row, table, Yaw_Governor_Right );
1214  tp = OPTIM_GET( row, table, Yaw_Governor_Left );
1215  computer.max_yaw_right = ::stof( tn.length() > 0 ? tn : t )*VS_PI/180.;
1216  computer.max_yaw_left = ::stof( tp.length() > 0 ? tp : t )*VS_PI/180.;
1217  t = OPTIM_GET( row, table, Pitch_Governor );
1218  tn = OPTIM_GET( row, table, Pitch_Governor_Up );
1219  tp = OPTIM_GET( row, table, Pitch_Governor_Down );
1220  computer.max_pitch_up = ::stof( tn.length() > 0 ? tn : t )*VS_PI/180.;
1221  computer.max_pitch_down = ::stof( tp.length() > 0 ? tp : t )*VS_PI/180.;
1222  t = OPTIM_GET( row, table, Roll_Governor );
1223  tn = OPTIM_GET( row, table, Roll_Governor_Right );
1224  tp = OPTIM_GET( row, table, Roll_Governor_Left );
1225  computer.max_roll_right = ::stof( tn.length() > 0 ? tn : t )*VS_PI/180.;
1226  computer.max_roll_left = ::stof( tp.length() > 0 ? tp : t )*VS_PI/180.;
1227  }
1228  static float game_accel = XMLSupport::parse_float( vs_config->getVariable( "physics", "game_accel", "1" ) );
1229  static float game_speed = XMLSupport::parse_float( vs_config->getVariable( "physics", "game_speed", "1" ) );
1230  limits.afterburn = ::stof( OPTIM_GET( row, table, Afterburner_Accel ) )*game_accel*game_speed;
1231  limits.forward = ::stof( OPTIM_GET( row, table, Forward_Accel ) )*game_accel*game_speed;
1232  limits.retro = ::stof( OPTIM_GET( row, table, Retro_Accel ) )*game_accel*game_speed;
1233  limits.lateral = .5
1234  *( ::stof( OPTIM_GET( row, table,
1235  Left_Accel ) )+::stof( OPTIM_GET( row, table, Right_Accel ) ) )*game_accel*game_speed;
1236  limits.vertical = .5
1237  *( ::stof( OPTIM_GET( row, table,
1238  Top_Accel ) )+::stof( OPTIM_GET( row, table, Bottom_Accel ) ) )*game_accel*game_speed;
1239  computer.max_combat_speed = ::stof( OPTIM_GET( row, table, Default_Speed_Governor ) )*game_speed;
1240  computer.max_combat_ab_speed = ::stof( OPTIM_GET( row, table, Afterburner_Speed_Governor ) )*game_speed;
1241  computer.itts = stob( OPTIM_GET( row, table, ITTS ), true );
1242  computer.radar.canlock = stob( OPTIM_GET( row, table, Can_Lock ), true );
1243  {
1244  // The Radar_Color column in the units.csv has been changed from a
1245  // boolean value to a string. The boolean values are supported for
1246  // backwardscompatibility.
1247  // When we save this setting, it is simply converted from an integer
1248  // number to a string, and we need to support this as well.
1249  std::string iffval = OPTIM_GET( row, table, Radar_Color );
1250  if ((iffval.empty()) || (iffval == "FALSE") || (iffval == "0"))
1251  {
1253  }
1254  else if ((iffval == "TRUE") || (iffval == "1"))
1255  {
1259  }
1260  else if (iffval == "THREAT")
1261  {
1266  }
1267  else if (iffval == "BUBBLE_THREAT")
1268  {
1274  }
1275  else if (iffval == "PLANE")
1276  {
1280  }
1281  else if (iffval == "PLANE_THREAT")
1282  {
1288  }
1289  else
1290  {
1291  unsigned int value = stoi(iffval, 0);
1292  if (value == 0)
1293  {
1294  // Unknown value
1295  assert(false);
1297  }
1298  else
1299  {
1300  computer.radar.capability = value;
1301  }
1302  }
1303  }
1304  computer.radar.maxrange = stof( OPTIM_GET( row, table, Radar_Range ), FLT_MAX );
1305  computer.radar.maxcone = cos( stof( OPTIM_GET( row, table, Max_Cone ), 180 )*VS_PI/180 );
1306  computer.radar.trackingcone = cos( stof( OPTIM_GET( row, table, Tracking_Cone ), 180 )*VS_PI/180 );
1307  computer.radar.lockcone = cos( stof( OPTIM_GET( row, table, Lock_Cone ), 180 )*VS_PI/180 );
1308  cloakmin = (int) (::stof( OPTIM_GET( row, table, Cloak_Min ) )*2147483136);
1309  if (cloakmin < 0) cloakmin = 0;
1310  pImage->cloakglass = XMLSupport::parse_bool( OPTIM_GET( row, table, Cloak_Glass ) );
1311  if ( (cloakmin&0x1) && !pImage->cloakglass )
1312  cloakmin -= 1;
1313  if ( (cloakmin&0x1) == 0 && pImage->cloakglass )
1314  cloakmin += 1;
1315  if ( !XMLSupport::parse_bool( OPTIM_GET( row, table, Can_Cloak ) ) )
1316  cloaking = -1;
1317  else
1318  cloaking = (int) (-2147483647)-1;
1319  pImage->cloakrate = (int) ( 2147483136.*::stof( OPTIM_GET( row, table, Cloak_Rate ) ) ); //short fix
1320  pImage->cloakenergy = ::stof( OPTIM_GET( row, table, Cloak_Energy ) );
1321  pImage->repair_droid = ::stoi( OPTIM_GET( row, table, Repair_Droid ) );
1322  pImage->ecm = ::stoi( OPTIM_GET( row, table, ECM_Rating ) );
1323 
1324  this->HeatSink = ::stof( OPTIM_GET( row, table, Heat_Sink_Rating ) );
1325  if (pImage->ecm < 0) pImage->ecm *= -1;
1326  if (pImage->cockpit_damage) {
1327  HudDamage( pImage->cockpit_damage, OPTIM_GET( row, table, Hud_Functionality ) );
1328  HudDamage( pImage->cockpit_damage+1+MAXVDUS+UnitImages< void >::NUMGAUGES, OPTIM_GET( row, table, Max_Hud_Functionality ) );
1329  }
1330  pImage->LifeSupportFunctionality = ::stof( OPTIM_GET( row, table, Lifesupport_Functionality ) );
1331  pImage->LifeSupportFunctionalityMax = ::stof( OPTIM_GET( row, table, Max_Lifesupport_Functionality ) );
1332  pImage->CommFunctionality = ::stof( OPTIM_GET( row, table, Comm_Functionality ) );
1333  pImage->CommFunctionalityMax = ::stof( OPTIM_GET( row, table, Max_Comm_Functionality ) );
1334  pImage->fireControlFunctionality = ::stof( OPTIM_GET( row, table, FireControl_Functionality ) );
1335  pImage->fireControlFunctionalityMax = ::stof( OPTIM_GET( row, table, Max_FireControl_Functionality ) );
1336  pImage->SPECDriveFunctionality = ::stof( OPTIM_GET( row, table, SPECDrive_Functionality ) );
1337  pImage->SPECDriveFunctionalityMax = ::stof( OPTIM_GET( row, table, Max_SPECDrive_Functionality ) );
1338  computer.slide_start = ::stoi( OPTIM_GET( row, table, Slide_Start ) );
1339  computer.slide_end = ::stoi( OPTIM_GET( row, table, Slide_End ) );
1340  UpgradeUnit( this, OPTIM_GET( row, table, Upgrades ) );
1341  {
1342  std::string tractorability = OPTIM_GET( row, table, Tractorability );
1343  unsigned char tflags;
1344  if ( !tractorability.empty() ) {
1345  tflags = tractorImmune;
1346  if (tractorability.find_first_of( "pP" ) != string::npos)
1347  tflags |= tractorPush;
1348  if (tractorability.find_first_of( "iI" ) != string::npos)
1349  tflags |= tractorIn;
1350  } else {tflags = tractorPush; } setTractorability( (enum tractorHow) tflags );
1351  }
1352  this->pImage->explosion_type = OPTIM_GET( row, table, Explosion );
1353  if ( pImage->explosion_type.get().length() ) {
1355  } else {
1356  static std::string expani = vs_config->getVariable( "graphics", "explosion_animation", "explosion_orange.ani" );
1357  cache_ani( expani );
1358  }
1359  AddLights( this, xml, OPTIM_GET( row, table, Light ) );
1360  xml.shieldmesh_str = OPTIM_GET( row, table, Shield_Mesh );
1361  if ( xml.shieldmesh_str.length() ) {
1362  addShieldMesh( &xml, xml.shieldmesh_str.c_str(), xml.unitscale, faction, getFlightgroup() );
1363  meshdata.back() = xml.shieldmesh;
1364  } else {
1365  static int shieldstacks = XMLSupport::parse_int( vs_config->getVariable( "graphics", "shield_detail", "16" ) );
1366  static std::string shieldtex = vs_config->getVariable( "graphics", "shield_texture", "shield.bmp" );
1367  static std::string shieldtechnique = vs_config->getVariable( "graphics", "shield_technique", "" );
1368  meshdata.back() = new SphereMesh( rSize(), shieldstacks, shieldstacks, shieldtex.c_str(), shieldtechnique, NULL, false, ONE, ONE );
1369  }
1370  meshdata.back()->EnableSpecialFX();
1371  //Begin the Pow-w-w-war Zone Collide Tree Generation
1372  {
1373  xml.rapidmesh_str = OPTIM_GET( row, table, Rapid_Mesh );
1374  vector< mesh_polygon >polies;
1375 
1376  std::string collideTreeHash = VSFileSystem::GetHashName( modification+"#"+row[0] );
1377  this->colTrees = collideTrees::Get( collideTreeHash );
1378  if (this->colTrees)
1379  this->colTrees->Inc();
1380  csOPCODECollider *colShield = NULL;
1381  string tmpname = row[0]; //key
1382  if (!this->colTrees) {
1383  string val;
1384  xml.hasColTree = 1;
1385  if ( ( val = OPTIM_GET( row, table, Use_Rapid ) ).length() )
1386  xml.hasColTree = XMLSupport::parse_bool( val );
1387  if (xml.shieldmesh) {
1388  if ( meshdata.back() ) {
1389  meshdata.back()->GetPolys( polies );
1390  colShield = new csOPCODECollider( polies );
1391  }
1392  }
1393  if ( xml.rapidmesh_str.length() )
1394  addRapidMesh( &xml, xml.rapidmesh_str.c_str(), xml.unitscale, faction, getFlightgroup() );
1395  else
1396  xml.rapidmesh = NULL;
1397  polies.clear();
1398  if (xml.rapidmesh)
1399  xml.rapidmesh->GetPolys( polies );
1400  csOPCODECollider *csrc = NULL;
1401  if (xml.hasColTree) {
1402  csrc = getCollideTree( Vector( 1, 1, 1 ),
1403  xml.rapidmesh
1404  ? &polies : NULL );
1405  }
1406  this->colTrees = new collideTrees( collideTreeHash,
1407  csrc,
1408  colShield );
1409  if (xml.rapidmesh && xml.hasColTree) {
1410  //if we have a special rapid mesh we need to generate things now
1411  for (unsigned int i = 1; i < collideTreesMaxTrees; ++i)
1412  if (!this->colTrees->rapidColliders[i]) {
1413  unsigned int which = 1<<i;
1414  this->colTrees->rapidColliders[i] =
1415  getCollideTree( Vector( 1, 1, which ),
1416  &polies );
1417  }
1418  }
1419  if (xml.rapidmesh) {
1420  delete xml.rapidmesh;
1421  xml.rapidmesh = NULL;
1422  }
1423  }
1424  }
1425  CheckAccessory( this ); //turns on the ceerazy rotation for any accessories
1426  this->setAverageGunSpeed();
1427 }
1428 
1429 CSVRow GetUnitRow( string filename, bool subu, int faction, bool readlast, bool &rread )
1430 {
1431  std::string hashname = filename+"__"+FactionUtil::GetFactionName( faction );
1432  for (int i = ( (int) unitTables.size() )-(readlast ? 1 : 2); i >= 0; --i) {
1433  unsigned int where;
1434  if ( unitTables[i]->RowExists( hashname, where ) ) {
1435  rread = true;
1436  return CSVRow( unitTables[i], where );
1437  } else if ( unitTables[i]->RowExists( filename, where ) ) {
1438  rread = true;
1439  return CSVRow( unitTables[i], where );
1440  }
1441  }
1442  rread = false;
1443  return CSVRow();
1444 }
1445 
1446 void Unit::WriteUnit( const char *modifications )
1447 {
1448  static bool UNITTAB = XMLSupport::parse_bool( vs_config->getVariable( "physics", "UnitTable", "false" ) );
1449  if (UNITTAB) {
1450  bool bad = false;
1451  if (!modifications) bad = true;
1452  if (!bad)
1453  if ( !strlen( modifications ) )
1454  bad = true;
1455  if (bad) {
1456  fprintf( stderr, "Cannot Write out unit file %s %s that has no filename\n", name.get().c_str(), csvRow.get().c_str() );
1457  return;
1458  }
1459  std::string savedir = modifications;
1461  VSFile f;
1462  VSError err = f.OpenCreateWrite( savedir+"/"+name+".csv", UnitFile );
1463  if (err > Ok) {
1464  fprintf( stderr, "!!! ERROR : Writing saved unit file : %s\n", f.GetFullPath().c_str() );
1465  return;
1466  }
1467  std::string towrite = WriteUnitString();
1468  f.Write( towrite.c_str(), towrite.length() );
1469  f.Close();
1470  } else {
1471  if (pImage->unitwriter)
1472  pImage->unitwriter->Write( modifications );
1473  for (un_iter ui = getSubUnits(); (*ui) != NULL; ++ui)
1474  (*ui)->WriteUnit( modifications );
1475  }
1476 }
1477 
1478 using XMLSupport::tostring;
1479 
1480 static void mapToStringVec( vsUMap< string, string >a, vector< string > &key, vector< string > &value )
1481 {
1482  for (vsUMap< string, string >::iterator i = a.begin(); i != a.end(); ++i) {
1483  key.push_back( i->first );
1484  value.push_back( i->second );
1485  }
1486 }
1487 static string tos( float val )
1488 {
1489  return XMLSupport::tostring( val );
1490 }
1491 
1492 static string tos( double val )
1493 {
1494  return XMLSupport::tostring( (float) val );
1495 }
1496 
1497 static string tos( unsigned int val )
1498 {
1499  return XMLSupport::tostring( val );
1500 }
1501 
1502 static string tos( bool val )
1503 {
1504  return XMLSupport::tostring( (int) val );
1505 }
1506 
1507 static string tos( int val )
1508 {
1509  return XMLSupport::tostring( val );
1510 }
1511 
1513 {
1514  static bool UNITTAB = XMLSupport::parse_bool( vs_config->getVariable( "physics", "UnitTable", "false" ) );
1515  string ret = "";
1516  if (UNITTAB) {
1517  //this is the fillin part
1518  //fixme
1519  for (int i = unitTables.size()-1; i >= 0; --i) {
1520  unsigned int where;
1521  string val;
1522  if ( unitTables[i]->RowExists( csvRow, where ) ) {
1523  CSVRow row( unitTables[i], where );
1524  vsUMap< string, string >unit;
1525  for (unsigned int jj = 0; jj < row.size(); ++jj)
1526  if (jj != 0)
1527  unit[row.getKey( jj )] = row[jj];
1528  //mutable things
1529  unit["Equipment_Space"] = XMLSupport::tostring( pImage->equipment_volume );
1530  unit["Hold_Volume"] = XMLSupport::tostring( pImage->CargoVolume );
1531  unit["Hidden_Hold_Volume"] = XMLSupport::tostring( pImage->HiddenCargoVolume );
1532  unit["Upgrade_Storage_Volume"] = XMLSupport::tostring( pImage->UpgradeVolume );
1533  string mountstr;
1534  double unitScale = stof( unit["Unit_Scale"], 1 );
1535  {
1536  //mounts
1537  for (unsigned int j = 0; j < mounts.size(); ++j) {
1538  char mnt[1024];
1539  Matrix m;
1540  Transformation tr( mounts[j].GetMountOrientation(),
1541  mounts[j].GetMountLocation().Cast() );
1542  tr.to_matrix( m );
1543  string printedname = mounts[j].type->weapon_name;
1544  if (mounts[j].status == Mount::DESTROYED || mounts[j].status == Mount::UNCHOSEN)
1545  printedname = "";
1546  mountstr += "{"+printedname+";"+XMLSupport::tostring( mounts[j].ammo )+";"+XMLSupport::tostring(
1547  mounts[j].volume )+";"+lookupMountSize( mounts[j].size );
1548  sprintf( mnt, ";%lf;%lf;%lf;%lf;%lf;%lf;%lf;%lf;%lf;%lf;%lf;%lf;%lf}",
1549  m.p.i/unitScale,
1550  m.p.j/unitScale,
1551  m.p.k/unitScale,
1552  (double) mounts[j].xyscale/unitScale,
1553  (double) mounts[j].zscale/unitScale,
1554  (double) m.getR().i,
1555  (double) m.getR().j,
1556  (double) m.getR().k,
1557  (double) m.getQ().i,
1558  (double) m.getQ().j,
1559  (double) m.getQ().k,
1560  (double) mounts[j].functionality,
1561  (double) mounts[j].maxfunctionality
1562  );
1563  mountstr += mnt;
1564  }
1565  unit["Mounts"] = mountstr;
1566  }
1567  {
1568  //subunits
1569  vector< SubUnitStruct >subunits = GetSubUnits( unit["Sub_Units"] );
1570  if ( subunits.size() ) {
1571  unsigned int k = 0;
1572  Unit *subun;
1573  for (; k < subunits.size(); ++k)
1574  subunits[k].filename = "destroyed_blank";
1575  k = 0;
1576  for (un_iter su = this->getSubUnits(); ( subun = (*su) ) != NULL; ++su, ++k) {
1577  unsigned int j = k;
1578  for (; j < subunits.size(); ++j)
1579  if ( (subun->Position()-subunits[j].pos).MagnitudeSquared() < .00000001 )
1580  //we've got a hit
1581  break;
1582  if ( j >= subunits.size() ) j = k;
1583  if ( j < subunits.size() )
1584  subunits[j].filename = subun->name;
1585  }
1586  string str;
1587  for (k = 0; k < subunits.size(); ++k) {
1588  char tmp[1024];
1589  sprintf( tmp, ";%lf;%lf;%lf;%lf;%lf;%lf;%lf;%lf;%lf;%lf}",
1590  subunits[k].pos.i,
1591  subunits[k].pos.j,
1592  subunits[k].pos.k,
1593  subunits[k].R.i,
1594  subunits[k].R.j,
1595  subunits[k].R.k,
1596  subunits[k].Q.i,
1597  subunits[k].Q.j,
1598  subunits[k].Q.k,
1599  ( (double) acos( subunits[k].restricted )*180./VS_PI ) );
1600  str += "{"+subunits[k].filename+tmp;
1601  }
1602  unit["Sub_Units"] = str;
1603  }
1604  }
1605  {
1606  string carg;
1607  for (unsigned int i = 0; i < numCargo(); ++i) {
1608  Cargo *c = &GetCargo( i );
1609  char tmp[2048];
1610  sprintf( tmp, ";%f;%d;%f;%f;%f;%f;;%s;%s}",
1611  c->price,
1612  c->quantity,
1613  c->mass,
1614  c->volume,
1615  c->functionality,
1616  c->maxfunctionality,
1617  c->mission ? "true" : "false",
1618  c->installed ? "true" : "false"
1619  );
1620  carg += "{"+c->GetContent()+";"+c->GetCategory()+tmp;
1621  }
1622  unit["Cargo"] = carg;
1623  }
1624  unit["Mass"] = tos( Mass );
1625  unit["Moment_Of_Inertia"] = tos( Momentofinertia );
1626  unit["Fuel_Capacity"] = tos( fuel );
1627  unit["Hull"] = tos( hull );
1628  unit["Spec_Interdiction"] = tos( specInterdiction );
1629  unit["Armor_Front_Top_Left"] = tos( armor.frontlefttop );
1630  unit["Armor_Front_Top_Right"] = tos( armor.frontrighttop );
1631  unit["Armor_Back_Top_Left"] = tos( armor.backlefttop );
1632  unit["Armor_Back_Top_Right"] = tos( armor.backrighttop );
1633  unit["Armor_Front_Bottom_Left"] = tos( armor.frontleftbottom );
1634  unit["Armor_Front_Bottom_Right"] = tos( armor.frontrightbottom );
1635  unit["Armor_Back_Bottom_Left"] = tos( armor.backleftbottom );
1636  unit["Armor_Back_Bottom_Right"] = tos( armor.backrightbottom );
1637  {
1638  unit["Shield_Front_Top_Right"] = "";
1639  unit["Shield_Front_Top_Left"] = "";
1640  unit["Shield_Back_Top_Right"] = "";
1641  unit["Shield_Back_Top_Left"] = "";
1642  unit["Shield_Front_Bottom_Right"] = "";
1643  unit["Shield_Front_Bottom_Left"] = "";
1644  unit["Shield_Back_Bottom_Right"] = "";
1645  unit["Shield_Back_Bottom_Left"] = "";
1646  switch (shield.number)
1647  {
1648  case 8:
1649  unit["Shield_Front_Top_Right"] = tos( shield.shield8.frontrighttopmax );
1650  unit["Shield_Front_Top_Left"] = tos( shield.shield8.frontlefttopmax );
1651  unit["Shield_Back_Top_Right"] = tos( shield.shield8.backrighttopmax );
1652  unit["Shield_Back_Top_Left"] = tos( shield.shield8.backlefttopmax );
1653  unit["Shield_Front_Bottom_Right"] = tos( shield.shield8.frontrightbottommax );
1654  unit["Shield_Front_Bottom_Left"] = tos( shield.shield8.frontleftbottommax );
1655  unit["Shield_Back_Bottom_Right"] = tos( shield.shield8.backrightbottommax );
1656  unit["Shield_Back_Bottom_Left"] = tos( shield.shield8.backleftbottommax );
1657  break;
1658  case 4:
1659  unit["Shield_Front_Top_Right"] = tos( shield.shield4fbrl.frontmax );
1660  unit["Shield_Back_Top_Right"] = tos( shield.shield4fbrl.backmax );
1661  unit["Shield_Front_Bottom_Right"] = tos( shield.shield4fbrl.rightmax );
1662  unit["Shield_Front_Bottom_Left"] = tos( shield.shield4fbrl.leftmax );
1663  break;
1664  case 2:
1665  unit["Shield_Front_Top_Right"] = tos( shield.shield2fb.frontmax );
1666  unit["Shield_Back_Top_Right"] = tos( shield.shield2fb.backmax );
1667  break;
1668  //NOTE: otherwise, no shields
1669  }
1670  }
1671  unit["Shield_Leak"] = tos( shield.leak/100.0 );
1672  unit["Shield_Recharge"] = tos( shield.recharge );
1673  unit["Shield_Efficiency"] = tos( shield.efficiency );
1674  unit["Warp_Capacitor"] = tos( maxwarpenergy );
1675  unit["Warp_Min_Multiplier"] = tos( graphicOptions.MinWarpMultiplier );
1676  unit["Warp_Max_Multiplier"] = tos( graphicOptions.MaxWarpMultiplier );
1677  unit["Primary_Capacitor"] = tos( maxenergy );
1678  unit["Reactor_Recharge"] = tos( recharge );
1679  unit["Jump_Drive_Present"] = tos( jump.drive >= -1 );
1680  unit["Jump_Drive_Delay"] = tos( jump.delay );
1681  unit["Wormhole"] = tos( pImage->forcejump != 0 );
1682  unit["Outsystem_Jump_Cost"] = tos( jump.energy );
1683  unit["Warp_Usage_Cost"] = tos( jump.insysenergy );
1684  unit["Afterburner_Usage_Cost"] = tos( afterburnenergy );
1685  unit["Afterburner_Type"] = tos( afterburntype );
1686  unit["Maneuver_Yaw"] = tos( limits.yaw*180/(VS_PI) );
1687  unit["Maneuver_Pitch"] = tos( limits.pitch*180/(VS_PI) );
1688  unit["Maneuver_Roll"] = tos( limits.roll*180/(VS_PI) );
1689  unit["Yaw_Governor_Right"] = tos( computer.max_yaw_right*180/VS_PI );
1690  unit["Yaw_Governor_Left"] = tos( computer.max_yaw_left*180/VS_PI );
1691  unit["Pitch_Governor_Up"] = tos( computer.max_pitch_up*180/VS_PI );
1692  unit["Pitch_Governor_Down"] = tos( computer.max_pitch_down*180/VS_PI );
1693  unit["Roll_Governor_Right"] = tos( computer.max_roll_right*180/VS_PI );
1694  unit["Roll_Governor_Left"] = tos( computer.max_roll_left*180/VS_PI );
1695  static float game_accel = XMLSupport::parse_float( vs_config->getVariable( "physics", "game_accel", "1" ) );
1696  static float game_speed = XMLSupport::parse_float( vs_config->getVariable( "physics", "game_speed", "1" ) );
1697  unit["Afterburner_Accel"] = tos( limits.afterburn/(game_accel*game_speed) );
1698  unit["Forward_Accel"] = tos( limits.forward/(game_accel*game_speed) );
1699  unit["Retro_Accel"] = tos( limits.retro/(game_accel*game_speed) );
1700  unit["Left_Accel"] = unit["Right_Accel"] = tos( limits.lateral/(game_accel*game_speed) );
1701  unit["Bottom_Accel"] = unit["Top_Accel"] = tos( limits.vertical/(game_accel*game_speed) );
1702  unit["Default_Speed_Governor"] = tos( computer.max_combat_speed/game_speed );
1703  unit["Afterburner_Speed_Governor"] = tos( computer.max_combat_ab_speed/game_speed );
1704  unit["ITTS"] = tos( computer.itts );
1705  unit["Can_Lock"] = tos( computer.radar.canlock );
1706  unit["Radar_Color"] = tos( computer.radar.capability );
1707  unit["Radar_Range"] = tos( computer.radar.maxrange );
1708  unit["Tracking_Cone"] = tos( acos( computer.radar.trackingcone )*180./VS_PI );
1709  unit["Max_Cone"] = tos( acos( computer.radar.maxcone )*180./VS_PI );
1710  unit["Lock_Cone"] = tos( acos( computer.radar.lockcone )*180./VS_PI );
1711  unit["Cloak_Min"] = tos( cloakmin/2147483136. );
1712  unit["Can_Cloak"] = tos( cloaking != -1 );
1713  unit["Cloak_Rate"] = tos( fabs( pImage->cloakrate/2147483136. ) );
1714  unit["Cloak_Energy"] = tos( pImage->cloakenergy );
1715  unit["Cloak_Glass"] = tos( pImage->cloakglass );
1716  unit["Repair_Droid"] = tos( pImage->repair_droid );
1717  unit["ECM_Rating"] = tos( pImage->ecm > 0 ? pImage->ecm : -pImage->ecm );
1718  unit["Hud_Functionality"] = WriteHudDamage( this );
1719  unit["Max_Hud_Functionality"] = WriteHudDamageFunc( this );
1720  unit["Heat_Sink_Rating"] = tos( this->HeatSink );
1721  unit["Lifesupport_Functionality"] = tos( pImage->LifeSupportFunctionality );
1722  unit["Max_Lifesupport_Functionality"] = tos( pImage->LifeSupportFunctionalityMax );
1723  unit["Comm_Functionality"] = tos( pImage->CommFunctionality );
1724  unit["Max_Comm_Functionality"] = tos( pImage->CommFunctionalityMax );
1725  unit["Comm_Functionality"] = tos( pImage->CommFunctionality );
1726  unit["Max_Comm_Functionality"] = tos( pImage->CommFunctionalityMax );
1727  unit["FireControl_Functionality"] = tos( pImage->fireControlFunctionality );
1728  unit["Max_FireControl_Functionality"] = tos( pImage->fireControlFunctionalityMax );
1729  unit["SPECDrive_Functionality"] = tos( pImage->SPECDriveFunctionality );
1730  unit["Max_SPECDrive_Functionality"] = tos( pImage->SPECDriveFunctionalityMax );
1731  unit["Slide_Start"] = tos( computer.slide_start );
1732  unit["Slide_End"] = tos( computer.slide_end );
1733  unit["Cargo_Import"] = unit["Upgrades"] = ""; //make sure those are empty
1734  {
1735  std::string trac;
1736  if ( isTractorable( tractorPush ) ) trac += "p";
1737  if ( isTractorable( tractorIn ) ) trac += "i";
1738  if ( trac.empty() ) trac = "-";
1739  unit["Tractorability"] = trac;
1740  }
1741  vector< string >keys, values;
1742  keys.push_back( "Key" );
1743  values.push_back( csvRow ); //key has to come first
1744  mapToStringVec( unit, keys, values );
1745  return writeCSV( keys, values );
1746  }
1747  }
1748  fprintf( stderr, "Failed to locate base mesh for %s %s %s\n", csvRow.get().c_str(), name.get().c_str(), fullname.c_str() );
1749  } else {
1750  if (pImage->unitwriter)
1751  ret = pImage->unitwriter->WriteString();
1752  for (un_iter ui = getSubUnits(); (*ui) != NULL; ++ui)
1753  ret = ret+( (*ui)->WriteUnitString() );
1754  }
1755  return ret;
1756 }
1757 
1759 {
1760  for (unsigned int i = 0; i < _Universe->numPlayers(); ++i) {
1761  Cockpit *cp = _Universe->AccessCockpit( i );
1762  std::vector< std::string > *addedcargoname = &cp->savegame->getMissionStringData( "master_part_list_content" );
1763  std::vector< std::string > *addedcargocat = &cp->savegame->getMissionStringData( "master_part_list_category" );
1764  std::vector< std::string > *addedcargovol = &cp->savegame->getMissionStringData( "master_part_list_volume" );
1765  std::vector< std::string > *addedcargoprice = &cp->savegame->getMissionStringData( "master_part_list_price" );
1766  std::vector< std::string > *addedcargomass = &cp->savegame->getMissionStringData( "master_part_list_mass" );
1767  std::vector< std::string > *addedcargodesc = &cp->savegame->getMissionStringData( "master_part_list_description" );
1768  for (unsigned int j = 0; j < addedcargoname->size(); ++j) {
1769  Cargo carg;
1770  carg.content = (*addedcargoname)[j];
1771  carg.category = ( j < addedcargocat->size() ? (*addedcargocat)[j] : std::string( "Uncategorized" ) );
1772  carg.volume = (j < addedcargovol->size() ? XMLSupport::parse_float( (*addedcargovol)[j] ) : 1.0);
1773  carg.price = (j < addedcargoprice->size() ? XMLSupport::parse_float( (*addedcargoprice)[j] ) : 0.0);
1774  carg.mass = (j < addedcargomass->size() ? XMLSupport::parse_float( (*addedcargomass)[j] ) : .01);
1775  carg.description = ( j < addedcargodesc->size() ? (*addedcargodesc)[j] : std::string( "No Description Added" ) );
1776  carg.quantity = 1;
1777  ret->GetImageInformation().cargo.push_back( carg );
1778  }
1779  }
1780  std::sort( ret->GetImageInformation().cargo.begin(), ret->GetImageInformation().cargo.end() );
1781  {
1782  Cargo last_cargo;
1783  for (int i = ret->numCargo()-1; i >= 0; --i) {
1784  if (ret->GetCargo( i ).content == last_cargo.content
1785  && ret->GetCargo( i ).category == last_cargo.category)
1786  ret->RemoveCargo( i, ret->GetCargo( i ).quantity, true );
1787  else
1788  last_cargo = ret->GetCargo( i );
1789  }
1790  }
1791 }
1792 
1794 {
1795  static std::string mpl = vs_config->getVariable( "data", "master_part_list", "master_part_list" );
1796  Unit *ret = new Unit();
1797  ret->name = "master_part_list";
1798  VSFileSystem::VSFile mplf;
1800  unsigned int i;
1801  if (err <= VSFileSystem::Ok) {
1802  CSVTable table( mplf, mplf.GetRoot() );
1803  mplf.Close();
1804  vsUMap< std::string, int >::const_iterator it;
1805  for (it = table.rows.begin(); it != table.rows.end(); ++it) {
1806  CSVRow row( &table, it->second );
1807  Cargo carg;
1808  carg.content = row["file"];
1809  carg.category = row["categoryname"];
1810  carg.volume = stof( row["volume"], 1 );
1811  carg.mass = stof( row["mass"], 1 );
1812  carg.quantity = 1;
1813  carg.price = stoi( row["price"], 1 );
1814  carg.description = row["description"];
1815  ret->GetImageInformation().cargo.push_back( carg );
1816  }
1817  }
1818  UpdateMasterPartList( ret );
1819  if ( !ret->GetCargo( "Pilot", i ) ) //required items
1820  ret->AddCargo( Cargo( "Pilot", "Contraband", 800, 1, .01, 1, 1.0, 1.0 ), true );
1821  if ( !ret->GetCargo( "Hitchhiker", i ) )
1822  ret->AddCargo( Cargo( "Hitchhiker", "Passengers", 42, 1, .01, 5.0, 1.0, 1.0 ), true );
1823  if ( !ret->GetCargo( "Slaves", i ) )
1824  ret->AddCargo( Cargo( "Slaves", "Contraband", 800, 1, .01, 1, 1, 1 ), true );
1825  return ret;
1826 }
1827