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_util_generic.cpp
Go to the documentation of this file.
1 #include <string>
2 #include "cmd/unit_generic.h"
3 #include "cmd/unit_util.h"
4 #include "configxml.h"
5 #include "vs_globals.h"
6 #include "gfx/cockpit_generic.h"
7 #include "universe_util.h"
8 #include "cmd/ai/docking.h"
9 #include "savegame.h"
10 #include "cmd/planet_generic.h"
11 #include "faction_generic.h"
12 #include "cmd/ai/fire.h"
13 #include "gfx/cockpit_generic.h"
14 #include "role_bitmask.h"
15 #include "lin_time.h"
16 #include "networking/netserver.h"
17 
19 #ifndef NO_GFX
20 #include "gfx/cockpit.h"
21 #endif
22 const Unit * makeTemplateUpgrade( string name, int faction ); //for percentoperational
23 const Unit * getUnitFromUpgradeName( const string &upgradeName, int myUnitFaction = 0 ); //for percentoperational
24 extern const char *DamagedCategory; //for percentoperational
25 extern bool isWeapon( std::string name ); //for percentoperational
26 using std::string;
27 extern Unit * getTopLevelOwner();
28 static bool nameIsAsteroid( std::string name )
29 {
30  if (name.length() < 8) return false;
31  return (name[0] == 'A' || name[0] == 'a')
32  && name[1] == 's'
33  && name[2] == 't'
34  && name[3] == 'e'
35  && name[4] == 'r'
36  && name[5] == 'o'
37  && name[6] == 'i'
38  && name[7] == 'd'
39  ;
40 }
41 
42 namespace UnitUtil
43 {
44 //
45 
46 template < typename T >
47 static inline T mymin( T a, T b )
48 {
49  return (a < b) ? a : b;
50 }
51 
52 template < typename T >
53 static inline T mymax( T a, T b )
54 {
55  return (a > b) ? a : b;
56 }
57 
58 static const string& getFgDirectiveCR( const Unit *my_unit );
59 
60 bool isAsteroid( const Unit *my_unit )
61 {
62  if (!my_unit) return false;
63  return my_unit->isUnit() == ASTEROIDPTR || nameIsAsteroid( my_unit->name );
64 }
65 
66 bool isCapitalShip( const Unit *my_unit )
67 {
68  if (!my_unit) return false;
69  static unsigned int capitaltypes = ROLES::getCapitalRoles();
70  return ( ( 1<<(unsigned int) my_unit->unitRole() )&capitaltypes ) != 0;
71 }
72 
73 bool hasDockingUnits( const Unit *my_unit )
74 {
75  if (!my_unit) return false;
76  return (my_unit->DockedOrDocking()&Unit::DOCKING_UNITS)
77  || ( my_unit->hasPendingClearanceRequests() );
78 }
79 
81 {
82  static const bool FORCE_TOP_PRIORITY = XMLSupport::parse_bool(
83  vs_config->getVariable( "physics", "priorities", "force_top_priority", "false" ) );
84  if (FORCE_TOP_PRIORITY || SERVER || Network)
85  return 1;
86  //Some other comment mentions these need special treatment for subunit scheduling
87  static const int PLAYER_PRIORITY = XMLSupport::parse_int(
88  vs_config->getVariable( "physics", "priorities", "player", "1" ) );
89  static const int MISSILE_PRIORITY = XMLSupport::parse_int(
90  vs_config->getVariable( "physics", "priorities", "missile", "1" ) );
91  static const int DOCKABLE_PRIORITY = XMLSupport::parse_int(
92  vs_config->getVariable( "physics", "priorities", "dockable", "1" ) );
93 
94  float rad = un->rSize();
95  clsptr untype = un->isUnit();
96  float cpdist = FLT_MAX;
97  float tooclose = 0;
98  unsigned int np = _Universe->numPlayers();
99  Cockpit *cockpit = _Universe->AccessCockpit();
100  static float fixed_system_orbit_priorities =
101  XMLSupport::parse_float( vs_config->getVariable( "physics", "fixed_system_priority_velocity_cutoff", "50" ) );
102  static const int SYSTEM_INSTALLATION_PRIORITY = XMLSupport::parse_int(
103  vs_config->getVariable( "physics", "priorities", "system_installation", "3" ) );
104  bool system_installation = un->owner == getTopLevelOwner();
105  bool force_system_installation_priority = false;
106  if (system_installation && un->Velocity.MagnitudeSquared() > fixed_system_orbit_priorities*fixed_system_orbit_priorities)
107  force_system_installation_priority = true;
108  for (unsigned int i = 0; i < np; ++i) {
109  Unit *player = _Universe->AccessCockpit( i )->GetParent();
110  if (player) {
111  if (force_system_installation_priority && player->activeStarSystem == un->activeStarSystem)
112  return SYSTEM_INSTALLATION_PRIORITY;
113  if ( un == player->Target() )
114  return PLAYER_PRIORITY;
115  float tmpdist = UnitUtil::getDistance( un, player );
116  if (tmpdist < cpdist) {
117  QVector relvel = un->GetVelocity()-player->GetVelocity();
118  QVector relpos = un->Position()-player->Position();
119  cockpit = _Universe->AccessCockpit( i );
120  cpdist = tmpdist;
121  float lowest_priority_time = SIM_QUEUE_SIZE*SIMULATION_ATOM;
122  if (relpos.Dot( relvel ) >= 0) {
123  //No need to be wary if they're getting away
124  tooclose =
125  2*(un->radial_size+player->radial_size)
126  +relvel.Magnitude()*lowest_priority_time;
127  }
128  }
129  }
130 #ifndef NO_GFX
132  if (cam) {
133  QVector relvel = un->GetVelocity()-cam->GetVelocity();
134  QVector relpos = un->Position()-cam->GetPosition();
135  double dist = relpos.Magnitude()-rad;
136  if (dist < cpdist) {
137  cpdist = dist;
138  Unit *parent = _Universe->AccessCockpit( i )->GetParent();
139  float lowest_priority_time = SIM_QUEUE_SIZE*SIMULATION_ATOM;
140  if (relpos.Dot( relvel ) >= 0)
141  //No need to be wary if they're getting away
142  tooclose =
143  2*( un->radial_size+(parent ? parent->radial_size : 0) )
144  +relvel.Magnitude()*lowest_priority_time;
145  }
146  }
147 #endif
148  if ( player == un
149  || (0 && _Universe->AccessCockpit( i )->GetSaveParent() == un) )
150  return PLAYER_PRIORITY;
151  }
152  if (untype == MISSILEPTR)
153  return MISSILE_PRIORITY;
154  if ( hasDockingUnits( un ) )
155  return DOCKABLE_PRIORITY;
156  static const int ASTEROID_PARENT_PRIORITY = XMLSupport::parse_int(
157  vs_config->getVariable( "physics", "priorities", "asteroid_parent", "1" ) );
158  static const int ASTEROID_HIGH_PRIORITY = XMLSupport::parse_int(
159  vs_config->getVariable( "physics", "priorities", "asteroid_high", "2" ) );
160  static const int ASTEROID_LOW_PRIORITY = XMLSupport::parse_int(
161  vs_config->getVariable( "physics", "priorities", "asteroid.low", "32" ) );
162  static const int HIGH_PRIORITY = XMLSupport::parse_int(
163  vs_config->getVariable( "physics", "priorities", "high", SERVER ? "1" : "2" ) );
164  static const int MEDIUMHIGH_PRIORITY = XMLSupport::parse_int(
165  vs_config->getVariable( "physics", "priorities", "mediumhigh", "4" ) );
166  static const int MEDIUM_PRIORITY = XMLSupport::parse_int(
167  vs_config->getVariable( "physics", "priorities", "medium", "8" ) );
168  static const int LOW_PRIORITY = XMLSupport::parse_int(
169  vs_config->getVariable( "physics", "priorities", "low", "32" ) );
170  static const int NOT_VISIBLE_COMBAT_HIGH = XMLSupport::parse_int(
171  vs_config->getVariable( "physics", "priorities", "notvisible_combat_high", "10" ) );
172  static const int NOT_VISIBLE_COMBAT_MEDIUM = XMLSupport::parse_int(
173  vs_config->getVariable( "physics", "priorities", "notvisible_combat_medium", "20" ) );
174  static const int NOT_VISIBLE_COMBAT_LOW = XMLSupport::parse_int(
175  vs_config->getVariable( "physics", "priorities", "notvisible_combat_low", "40" ) );
176  static const int NO_ENEMIES = XMLSupport::parse_int(
177  vs_config->getVariable( "physics", "priorities", "no_enemies", "64" ) );
178  static const int INERT_PRIORITY = XMLSupport::parse_int(
179  vs_config->getVariable( "physics", "priorities", "inert", "64" ) );
180  static const float _PLAYERTHREAT_DISTANCE_FACTOR = XMLSupport::parse_float(
181  vs_config->getVariable( "physics", "priorities", "playerthreat_distance_factor", "2" ) );
182  static const float _THREAT_DISTANCE_FACTOR = XMLSupport::parse_float(
183  vs_config->getVariable( "physics", "priorities", "threat_distance_factor", "1" ) );
184  static const float DYNAMIC_THROTTLE_MINFACTOR = XMLSupport::parse_float(
185  vs_config->getVariable( "physics", "priorities", "dynamic_throttle.mindistancefactor", "0.25" ) );
186  static const float DYNAMIC_THROTTLE_MAXFACTOR = XMLSupport::parse_float(
187  vs_config->getVariable( "physics", "priorities", "dynamic_throttle.maxdistancefactor", "4" ) );
188  static const float DYNAMIC_THROTTLE_TARGETFPS = XMLSupport::parse_float(
189  vs_config->getVariable( "physics", "priorities", "dynamic_throttle.targetfps", "30" ) );
190  static const float DYNAMIC_THROTTLE_TARGETELAPSEDTIME = 1.f/DYNAMIC_THROTTLE_TARGETFPS;
191  static float DYNAMIC_THROTTLE_FACTOR = 1.f;
192  static float lastThrottleAdjust = 0;
193  if (UniverseUtil::GetGameTime() != lastThrottleAdjust) {
194  lastThrottleAdjust = UniverseUtil::GetGameTime();
195  float newfactor = DYNAMIC_THROTTLE_FACTOR*DYNAMIC_THROTTLE_TARGETELAPSEDTIME/GetElapsedTime();
196  newfactor = mymax( DYNAMIC_THROTTLE_MINFACTOR, mymin( DYNAMIC_THROTTLE_MAXFACTOR, newfactor ) );
197  DYNAMIC_THROTTLE_FACTOR = (newfactor*GetElapsedTime()+DYNAMIC_THROTTLE_FACTOR)/( 1.0+GetElapsedTime() );
198  }
199  const float PLAYERTHREAT_DISTANCE_FACTOR = _PLAYERTHREAT_DISTANCE_FACTOR*DYNAMIC_THROTTLE_FACTOR;
200  const float THREAT_DISTANCE_FACTOR = _THREAT_DISTANCE_FACTOR*DYNAMIC_THROTTLE_FACTOR;
201  Unit *parent = cockpit->GetParent();
202  float gun_range = 0;
203  float missile_range = 0;
204  float dist = cpdist;
205  if (parent) {
206  float speed = 0;
207  parent->getAverageGunSpeed( speed, gun_range, missile_range );
208  }
209  static int cargofac = FactionUtil::GetFactionIndex( "cargo" );
210  int upfac = FactionUtil::GetUpgradeFaction();
211  int neutral = FactionUtil::GetNeutralFaction();
213  //Asteroids do scheduling themselves within subunits, so...
214  //...only one caveat: units with their own internal scheduling
215  //must have constant priority... otherwise, big mess when changing
216  //priorities. I'll think of one way to overcome this...
217  switch (un->schedule_priority)
218  {
220  break;
222  return ASTEROID_PARENT_PRIORITY;
223 
224  case Unit::scheduleRoid:
225  if (dist < tooclose)
226  return ASTEROID_HIGH_PRIORITY;
227  else
228  return ASTEROID_LOW_PRIORITY;
229  }
230  }
231  if ( UnitUtil::isAsteroid( un ) ) {
232  //some mods don't do the scheduling--still want correctness
233  static std::string blah = vs_config->getVariable( "physics", "priorities", "min_asteroid_distance", "none" );
234  static float too_close_asteroid = (blah == "none") ? tooclose : XMLSupport::parse_float( blah );
235  if (dist < too_close_asteroid)
236  return ASTEROID_HIGH_PRIORITY;
237  else
238  return ASTEROID_LOW_PRIORITY;
239  }
240  Unit *targ = un->Target();
241  if ( _Universe->isPlayerStarship( targ ) ) {
242  if ( UnitUtil::getDistance( targ, un ) <= PLAYERTHREAT_DISTANCE_FACTOR*mymax( gun_range, missile_range ) )
243  return un->isJumppoint() ? PLAYER_PRIORITY : HIGH_PRIORITY;
244  else //Needs to accurately collide with it...
245  return MEDIUMHIGH_PRIORITY;
246  }
248  static float compwarprampuptime =
249  XMLSupport::parse_float( vs_config->getVariable( "physics", "computerwarprampuptime", "10" ) ); //for the heck of it. NOTE, variable also in unit_generic.cpp
250  static float warprampdowntime = XMLSupport::parse_float( vs_config->getVariable( "physics", "warprampdowntime", "0.5" ) );
251  float lowest_priority_time = SIM_QUEUE_SIZE*SIMULATION_ATOM;
252 
253  float time_ramped = compwarprampuptime-un->graphicOptions.RampCounter;
254  if (un->graphicOptions.InWarp == 0)
255  time_ramped = warprampdowntime-un->graphicOptions.RampCounter;
256  if (un->graphicOptions.WarpRamping || time_ramped < lowest_priority_time)
257  return MEDIUM_PRIORITY;
258  if (dist < gun_range)
259  return MEDIUM_PRIORITY;
260  if (time_ramped < lowest_priority_time*2)
261  return LOW_PRIORITY;
262  //else defer decision
263  }
264  if (system_installation || un->faction == cargofac || un->faction == upfac || un->faction == neutral) {
265  if (dist < tooclose) {
266  return MEDIUM_PRIORITY;
267  } else {
268  if (system_installation)
269  return SIM_QUEUE_SIZE/ORBIT_PRIORITY; //so that the averaging can still keep track of parent jumps
270  return INERT_PRIORITY;
271  }
272  }
273  const string &obj = UnitUtil::getFgDirective( un );
274  if ( !( obj.length() == 0 || (obj.length() >= 1 && obj[0] == 'b') ) )
275  return MEDIUM_PRIORITY;
276  if ( dist < 2*PLAYERTHREAT_DISTANCE_FACTOR*mymax( missile_range, gun_range ) ) {
277  if (dist < tooclose*PLAYERTHREAT_DISTANCE_FACTOR)
278  return MEDIUMHIGH_PRIORITY;
279  else if (dist < 2*gun_range*PLAYERTHREAT_DISTANCE_FACTOR)
280  return MEDIUM_PRIORITY;
281  else
282  return LOW_PRIORITY;
283  }
284  if (targ) {
285  float speed;
286  un->getAverageGunSpeed( speed, gun_range, missile_range );
287  double distance = UnitUtil::getDistance( un, targ );
288  if (distance <= 2*gun_range*THREAT_DISTANCE_FACTOR)
289  return NOT_VISIBLE_COMBAT_HIGH;
290  if (distance < 2*missile_range*THREAT_DISTANCE_FACTOR)
291  return NOT_VISIBLE_COMBAT_MEDIUM;
292  return NOT_VISIBLE_COMBAT_LOW;
293  }
294  if (dist < tooclose*THREAT_DISTANCE_FACTOR)
295  return MEDIUMHIGH_PRIORITY;
296  else //May not have weapons (hence missile_range|gun_range == 0)
297  return NO_ENEMIES;
298 }
299 
300 void orbit( Unit *my_unit, Unit *orbitee, float speed, QVector R, QVector S, QVector center )
301 {
302  if (my_unit) {
303  my_unit->PrimeOrders( new PlanetaryOrbit( my_unit, speed/( 3.1415926536*( S.Magnitude()+R.Magnitude() ) ), 0, R, S,
304  center, orbitee ) );
305  if (orbitee)
306  if (orbitee->isUnit() == PLANETPTR)
307  ( (Planet*) orbitee )->AddSatellite( my_unit );
308  if ( my_unit->faction != FactionUtil::GetFactionIndex( "neutral" ) ) {
309  Order *tmp = new Orders::FireAt( 15.0 );
310  my_unit->EnqueueAI( tmp );
311  my_unit->SetTurretAI();
312  }
313  my_unit->SetOwner( getTopLevelOwner() );
314  }
315 }
316 
317 string getFactionName( const Unit *my_unit )
318 {
319  if (!my_unit) return "";
320  return FactionUtil::GetFaction( my_unit->faction );
321 }
322 
323 int getFactionIndex( const Unit *my_unit )
324 {
325  if (!my_unit) return 0;
326  return my_unit->faction;
327 }
328 
329 void setFactionIndex( Unit *my_unit, int factionname )
330 {
331  if (!my_unit) return;
332  my_unit->SetFaction( factionname );
333 }
334 
335 void setFactionName( Unit *my_unit, string factionname )
336 {
337  if (!my_unit) return;
338  my_unit->SetFaction( FactionUtil::GetFactionIndex( factionname ) );
339 }
340 
341 float getFactionRelation( const Unit *my_unit, const Unit *their_unit )
342 {
343  float relation = FactionUtil::GetIntRelation( my_unit->faction, their_unit->faction );
344  int my_cp = _Universe->whichPlayerStarship( my_unit );
345  int their_cp = _Universe->whichPlayerStarship( their_unit );
346  if (my_cp != -1)
347  relation += UniverseUtil::getRelationModifierInt( my_cp, their_unit->faction );
348  else if (their_cp != -1) /* The question is: use an else? */
349  relation += UniverseUtil::getRelationModifierInt( their_cp, my_unit->faction );
350  return relation;
351 }
352 
353 float getRelationToFaction( const Unit *my_unit, int other_faction )
354 {
355  float relation = FactionUtil::GetIntRelation( my_unit->faction, other_faction );
356  int my_cp = _Universe->whichPlayerStarship( my_unit );
357  if (my_cp != -1)
358  relation += UniverseUtil::getRelationModifierInt( my_cp, other_faction );
359  return relation;
360 }
361 
362 float getRelationFromFaction( const Unit *their_unit, int my_faction )
363 {
364  float relation = FactionUtil::GetIntRelation( my_faction, their_unit->faction );
365  int their_cp = _Universe->whichPlayerStarship( their_unit );
366  if (their_cp != -1)
367  relation += UniverseUtil::getRelationModifierInt( their_cp, my_faction );
368  return relation;
369 }
370 
371 string getName( const Unit *my_unit )
372 {
373  if (!my_unit) return "";
374  return my_unit->name;
375 }
376 
377 void setName( Unit *my_unit, string name )
378 {
379  if (!my_unit) return;
380  my_unit->name = name;
381 }
382 
383 void SetHull( Unit *my_unit, float newhull )
384 {
385  if (!my_unit) return;
386  my_unit->hull = newhull;
387 }
388 
389 float getCredits( const Unit *my_unit )
390 {
391  if (!my_unit) return 0;
392  Cockpit *tmp;
393  float viret = 0;
394  if ( ( tmp = _Universe->isPlayerStarship( my_unit ) ) )
395  viret = tmp->credits;
396  return viret;
397 }
398 
399 void addCredits( const Unit *my_unit, float credits )
400 {
401  if (!my_unit) return;
402  Cockpit *tmp;
403  if ( ( tmp = _Universe->isPlayerStarship( my_unit ) ) ) {
404  tmp->credits += credits;
405  if (SERVER)
406  VSServer->sendCredits( my_unit->GetSerial(), tmp->credits );
407  }
408 }
409 
410 const string& getFlightgroupNameCR( const Unit *my_unit )
411 {
412  static const string empty_string;
413  if (!my_unit) return empty_string;
414  Flightgroup *fg = my_unit->getFlightgroup();
415  if (fg)
416  return fg->name;
417 
418  else
419  return empty_string;
420 }
421 
422 string getFlightgroupName( const Unit *my_unit )
423 {
424  return getFlightgroupNameCR( my_unit );
425 }
426 
428 {
429  if (!my_unit) return 0;
430  class Flightgroup*fg = my_unit->getFlightgroup();
431  Unit *ret_unit = fg ? fg->leader.GetUnit() : my_unit;
432  if (!ret_unit)
433  ret_unit = 0;
434  return ret_unit;
435 }
436 
437 bool setFlightgroupLeader( Unit *my_unit, Unit *un )
438 {
439  if (!my_unit || !un) return false;
440  if ( my_unit->getFlightgroup() ) {
441  my_unit->getFlightgroup()->leader.SetUnit( un );
442  return true;
443  } else {
444  return false;
445  }
446 }
447 
448 string getFgDirective( const Unit *my_unit )
449 {
450  return getFgDirectiveCR( my_unit );
451 }
452 
453 static const string& getFgDirectiveCR( const Unit *my_unit )
454 {
455  static string emptystr;
456  static string fgdirdef( "b" );
457  if (!my_unit)
458  return emptystr;
459  if ( my_unit->getFlightgroup() )
460  return my_unit->getFlightgroup()->directive;
461 
462  else
463  return fgdirdef;
464 }
465 
466 bool setFgDirective( Unit *my_unit, string inp )
467 {
468  if (!my_unit) return false;
469  if (my_unit->getFlightgroup() != NULL) {
470  my_unit->getFlightgroup()->directive = inp;
471  return true;
472  }
473  return false;
474 }
475 
476 int getFgSubnumber( Unit *my_unit )
477 {
478  if (!my_unit) return -1;
479  return my_unit->getFgSubnumber();
480 }
481 
482 int removeCargo( Unit *my_unit, string s, int quantity, bool erasezero )
483 {
484  if (!my_unit) return 0;
485  unsigned int index;
486  if ( my_unit->GetCargo( s, index ) )
487  quantity = my_unit->RemoveCargo( index, quantity, erasezero );
488  else
489  quantity = 0;
490  return quantity;
491 }
492 
494 {
495  if (un == NULL) return;
496  if (Network) return;
497  un->ReduceToTemplate();
498  unsigned int i;
499  for (i = 0; i < un->numCargo(); ++i) {
500  Cargo *c = &un->GetCargo( i );
501  if (c->GetCategory().find( "upgrades" ) == 0 && c->GetCategory().find( DamagedCategory ) != 0) {
502  if (c->GetContent().find( "mult_" ) != 0
503  && c->GetContent().find( "add_" ) != 0)
504  un->Upgrade( c->content, 0, 0, true, false );
505  }
506  }
507  for (i = 0; i < un->numCargo(); ++i) {
508  Cargo *c = &un->GetCargo( i );
509  if (c->GetCategory().find( "upgrades" ) == 0 && c->GetCategory().find( DamagedCategory ) != 0) {
510  if (c->GetContent().find( "add_" ) == 0)
511  for (int j = 0; j < c->quantity; ++j)
512  un->Upgrade( c->content, 0, 0, true, false );
513  }
514  }
515  for (i = 0; i < un->numCargo(); ++i) {
516  Cargo *c = &un->GetCargo( i );
517  if (c->GetCategory().find( "upgrades" ) == 0 && c->GetCategory().find( DamagedCategory ) != 0) {
518  if (c->GetContent().find( "mult_" ) == 0)
519  for (int j = 0; j < c->quantity; ++j)
520  un->Upgrade( c->content, 0, 0, true, false );
521  }
522  }
523 }
524 
525 bool repair( Unit *my_unit )
526 {
527  if (!my_unit)
528  return false;
529  return my_unit->RepairUpgrade();
530 }
531 
532 float upgrade( Unit *my_unit, string file, int mountoffset, int subunitoffset, bool force, bool loop_through_mounts )
533 {
534  if (!my_unit) return 0;
535  double percentage = 0;
536  percentage = my_unit->Upgrade( file, mountoffset, subunitoffset, force, loop_through_mounts );
537  my_unit->SetTurretAI();
538  return percentage;
539 }
540 
541 int removeWeapon( Unit *my_unit, string weapon_name, int mountoffset, bool loop )
542 {
543  if (!my_unit) return -1;
544  int maxmount = my_unit->mounts.size();
545  int max = maxmount+mountoffset;
546  for (int loopi = mountoffset; loopi < max; ++loopi) {
547  int i = loopi%maxmount;
548  if ( my_unit->mounts[i].type->weapon_name == weapon_name
549  && (my_unit->mounts[i].status == Mount::ACTIVE || my_unit->mounts[i].status == Mount::INACTIVE) ) {
550  my_unit->mounts[i].status = Mount::UNCHOSEN;
551  return i;
552  }
553  }
554  return -1;
555 }
556 
557 int addCargo( Unit *my_unit, Cargo carg )
558 {
559  if (!my_unit) return 0;
560  int i;
561  for (i = carg.quantity; i > 0 && !my_unit->CanAddCargo( carg ); i--)
562  carg.quantity = i;
563  if (i > 0) {
564  carg.quantity = i;
565  my_unit->AddCargo( carg );
566  } else {
567  carg.quantity = 0;
568  }
569  return carg.quantity;
570 }
571 
572 int forceAddCargo( Unit *my_unit, Cargo carg )
573 {
574  if (!my_unit) return 0;
575  my_unit->AddCargo( carg );
576  return carg.quantity;
577 }
578 
579 int hasCargo( const Unit *my_unit, string mycarg )
580 {
581  if (!my_unit) return 0;
582  unsigned int i;
583  const Cargo *c = my_unit->GetCargo( mycarg, i );
584  if (c == NULL)
585  return 0;
586  return c->quantity;
587 }
588 
589 bool JumpTo( Unit *unit, string system )
590 {
591  if (unit != NULL)
592  return unit->getStarSystem()->JumpTo( unit, NULL, system );
593  else
594  return false;
595 }
596 
597 string getUnitSystemFile( const Unit *un )
598 {
599  if (!un)
601  const StarSystem *ss = un->getStarSystem();
602  return ss->getFileName();
603 }
604 
605 bool incrementCargo( Unit *my_unit, float percentagechange, int quantity )
606 {
607  if (!my_unit) return false;
608  if (my_unit->numCargo() > 0) {
609  unsigned int index;
610  index = rand()%my_unit->numCargo();
611  Cargo c( my_unit->GetCargo( index ) );
612  c.quantity = quantity;
613  if (c.price != 0) {
614  if ( my_unit->CanAddCargo( c ) ) {
615  my_unit->AddCargo( c );
616  my_unit->GetCargo( index ).price *= percentagechange;
617  return true;
618  }
619  }
620  }
621  return false;
622 }
623 
624 bool decrementCargo( Unit *my_unit, float percentagechange )
625 {
626  if (!my_unit) return false;
627  if (my_unit->numCargo() > 0) {
628  unsigned int index;
629  index = rand()%my_unit->numCargo();
630  if ( my_unit->RemoveCargo( index, 1, false ) )
631  my_unit->GetCargo( index ).price *= percentagechange;
632  return true;
633  }
634  return false;
635 }
636 
637 Cargo GetCargoIndex( const Unit *my_unit, int index )
638 {
639  if (my_unit)
640  if ( index >= 0 && (unsigned int) index < my_unit->numCargo() )
641  return my_unit->GetCargo( index );
642  Cargo ret;
643  ret.quantity = 0;
644  return ret;
645 }
646 
647 Cargo GetCargo( const Unit *my_unit, std::string cargname )
648 {
649  if (my_unit) {
650  unsigned int indx = 0;
651  const Cargo *cargptr = my_unit->GetCargo( cargname, indx );
652  if (cargptr && indx >= 0)
653  return *cargptr;
654  }
655  Cargo ret;
656  ret.quantity = 0;
657  return ret;
658 }
659 
660 bool isDockableUnit( const Unit *my_unit )
661 {
662  if (!my_unit) return false;
663  return (
664  (
665  my_unit->isPlanet()
666  && !isSun( my_unit )
667  && isSignificant( my_unit )
668  && !my_unit->isJumppoint()
669  )
670  || (my_unit->isUnit() == UNITPTR)
671  || (getFlightgroupName( my_unit ) == "Base")
672  )
673  && (my_unit->DockingPortLocations().size() > 0);
674 }
675 
676 bool isCloseEnoughToDock( const Unit *my_unit, const Unit *un )
677 {
678  static bool superdock = XMLSupport::parse_bool( vs_config->getVariable( "physics", "dock_within_base_shield", "false" ) );
679  float dis =
680  (un->isUnit() == PLANETPTR || superdock) ? UnitUtil::getSignificantDistance( my_unit, un ) : UnitUtil::getDistance(
681  my_unit,
682  un );
683  if ( dis < un->rSize() )
684  return true;
685  return false;
686 }
687 
688 float getDistance( const Unit *my_unit, const Unit *un )
689 {
690  if (my_unit == NULL || un == NULL)
691  return FLT_MAX;
692  return ( my_unit->Position()-un->Position() ).Magnitude()-my_unit->rSize()-un->rSize();
693 }
694 
695 float getSignificantDistance( const Unit *un, const Unit *sig )
696 {
697  if (un == NULL || sig == NULL)
698  return FLT_MAX;
699  float dist = getDistance( un, sig );
700 
701  float planetpct = UniverseUtil::getPlanetRadiusPercent();
702  if ( sig->isPlanet() )
703  dist = dist-(sig->rSize()*planetpct);
704  if ( un->isPlanet() )
705  dist = dist-(un->rSize()*planetpct);
706  return dist;
707 }
708 
709 bool isSun( const Unit *my_unit )
710 {
711  if (!my_unit) return false;
712  if ( my_unit->isJumppoint() )
713  return false;
714  bool res = false;
715  res = my_unit->isPlanet();
716  if (res)
717  res = ( (Planet*) my_unit )->hasLights();
718  return res;
719 }
720 
721 bool isSignificant( const Unit *my_unit )
722 {
723  if (!my_unit) return false;
724  bool res = false;
725  clsptr typ = my_unit->isUnit();
726  const string &s = getFlightgroupNameCR( my_unit );
727  res = (typ == PLANETPTR || typ == ASTEROIDPTR || typ == NEBULAPTR || s == "Base");
728  return res && !isSun( my_unit );
729 }
730 
731 int isPlayerStarship( const Unit *un )
732 {
733  return _Universe->whichPlayerStarship( un );
734 }
735 
736 void setSpeed( Unit *my_unit, float speed )
737 {
738  if (my_unit)
739  my_unit->GetComputerData().set_speed = speed;
740 }
741 
742 float maxSpeed( const Unit *my_unit )
743 {
744  if (!my_unit)
745  return 0;
746  return my_unit->ViewComputerData().max_speed();
747 }
748 float maxAfterburnerSpeed( const Unit *my_unit )
749 {
750  if (!my_unit)
751  return 0;
752  return my_unit->ViewComputerData().max_ab_speed();
753 }
754 
755 void setECM( Unit *my_unit, int NewECM )
756 {
757  //short fix
758  if (!my_unit)
759  return;
760  my_unit->GetImageInformation().ecm = NewECM;
761 }
762 
763 int getECM( const Unit *my_unit )
764 {
765  //short fix
766  if (!my_unit)
767  return 0;
768  return my_unit->computer.ecmactive ? my_unit->pImage->ecm : 0;
769 }
770 
771 static bool ishere( const Unit *par, const Unit *look )
772 {
773  const Unit *un;
774  for (un_kiter uniter = par->viewSubUnits(); (un = *uniter); ++uniter) {
775  if (un == look)
776  return true;
777  if ( un != par && ishere( un, look ) )
778  return true;
779  }
780  return false;
781 }
782 
783 Unit * owner( const Unit *un )
784 {
785  Unit *found = NULL;
786  Unit *tmp;
787  for (UniverseUtil::PythonUnitIter uniter = UniverseUtil::getUnitList(); (tmp = *uniter); ++uniter)
788  if ( tmp == un || ishere( tmp, un ) ) {
789  found = tmp;
790  break;
791  }
792  return found;
793 }
794 
795 void performDockingOperations( Unit *un, Unit *unitToDockWith, int actually_dock )
796 {
797  if (un && unitToDockWith) {
798  Order *ai = un->aistate;
799  un->aistate = NULL;
800  un->PrimeOrders( new Orders::DockingOps( unitToDockWith, ai, actually_dock, true ) );
801  }
802 }
803 
804 float PercentOperational( Unit *un, std::string name, std::string category, bool countHullAndArmorAsFull )
805 {
806  if (!un) return 0;
807  if (category.find( DamagedCategory ) == 0)
808  return 0.0f;
809  const Unit *upgrade = getUnitFromUpgradeName( name, un->faction );
810  if (!upgrade) return 1.0f;
811  if ( isWeapon( category ) ) {
812  static std::string loadfailed( "LOAD_FAILED" );
813  if ( upgrade->GetNumMounts() ) {
814  const Mount *mnt = &upgrade->mounts[0];
815  unsigned int nummounts = un->GetNumMounts();
816  for (unsigned int i = 0; i < nummounts; ++i)
817  if (mnt->type->weapon_name == un->mounts[i].type->weapon_name) {
818  if (un->mounts[i].status == Mount::DESTROYED)
819  return 0.0;
820  if (un->mounts[i].functionality < 1.0f)
821  return un->mounts[i].functionality;
822  }
823  }
824  } else if (name.find( "add_" ) != 0 && name.find( "mult_" ) != 0) {
825  float armor[8];
826  upgrade->ArmorData( armor );
827  if (upgrade->GetHull() > 1 || armor[0] || armor[1] || armor[2] || armor[3] || armor[4] || armor[5] || armor[6]
828  || armor[7])
829  if (countHullAndArmorAsFull)
830  return 1.0f;
831  double percent = 0;
832  if ( un->canUpgrade( upgrade, -1, -1, 0, true, percent, makeTemplateUpgrade( un->name, un->faction ), false ) ) {
833  if (percent > 0 && percent < 1)
834  return percent;
835  else if (percent >= 1) //FIXME workaround for sensors -- see below comment, not sure why sensors report erroneous functional percentage
836  return 1.0;
837  else return .5; //FIXME does not interact well with radar type
838  } else if (percent > 0) {
839  return percent;
840  }
841  }
842  return 1.0;
843 }
844 
846 {
847  if (un && mission->runtime.pymissions)
848  mission->runtime.pymissions->relevant_units.push_back( new UnitContainer( un ) );
849 }
850 
851 //
852 }
853