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_generic.cpp
Go to the documentation of this file.
1 // -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*-
2 
3 #include "config.h"
4 #include <set>
5 #include "configxml.h"
6 #include "audiolib.h"
7 #include "unit_generic.h"
8 #include "beam.h"
9 #include "lin_time.h"
10 #include "xml_serializer.h"
11 #include "vsfilesystem.h"
12 #include "file_main.h"
13 #include "universe_util.h"
14 #include "unit_util.h"
15 #include "script/mission.h"
16 #include "script/flightgroup.h"
17 #include "cmd/ai/fire.h"
18 #include "cmd/ai/turretai.h"
19 #include "cmd/ai/communication.h"
20 #include "cmd/ai/navigation.h"
21 #include "cmd/ai/script.h"
22 #include "cmd/ai/missionscript.h"
23 #include "cmd/ai/flybywire.h"
24 #include "cmd/ai/aggressive.h"
25 #include "python/python_class.h"
26 #include "cmd/unit_factory.h"
27 #include "missile_generic.h"
28 #include "gfx/cockpit_generic.h"
29 #include "gfx/vsbox.h"
30 #include <algorithm>
31 #include "cmd/ai/ikarus.h"
32 #include "role_bitmask.h"
33 #include "unit_const_cache.h"
34 #include "gfx/warptrail.h"
35 #include "networking/netserver.h"
36 #include "networking/netclient.h"
37 #include "gfx/cockpit_generic.h"
38 #include "universe_generic.h"
39 #include "csv.h"
40 #include "vs_random.h"
41 #include "galaxy_xml.h"
42 #include "gfx/camera.h"
43 
44 #ifdef _WIN32
45 #define strcasecmp stricmp
46 #endif
47 
48 #include "unit_find.h"
49 #include "pilot.h"
50 
51 #include "vsfilesystem.h"
52 #include <iostream>
53 #define DEBUG_MESH_ANI
54 
55 //cannot seem to get min and max working properly across win and lin any other way...
56 static float mymax( float a, float b )
57 {
58  return a < b ? b : a;
59 }
60 static float mymin( float a, float b )
61 {
62  return a < b ? a : b;
63 }
64 
65 using namespace Orders;
66 
67 extern void DestroyMount( Mount* );
68 
70 {
71  pos = v;
72 }
73 
75 {
76  orient = t;
77 }
78 
79 void Unit::SetNetworkMode( bool mode )
80 {
81  networked = mode;
82 }
83 
85 {
86  serial = s;
87 }
88 
90 {
91  FaceCamera = Animating = missilelock = InWarp = unused1 = WarpRamping = NoDamageParticles = 0;
92  specInterdictionOnline = 1;
93  NumAnimationPoints = 0;
94  RampCounter = 0;
95  MinWarpMultiplier = MaxWarpMultiplier = 1;
96 }
97 
99 {
100  graphicOptions.FaceCamera = 1;
101 }
102 
103 void Unit::attackPreference( unsigned char c )
104 {
105  attack_preference = c;
106 }
107 
108 void Unit::unitRole( unsigned char c )
109 {
110  unit_role = c;
111 }
112 
114 {
115  nebula = neb;
116  if ( !SubUnits.empty() ) {
117  un_fiter iter = SubUnits.fastIterator();
118  Unit *un;
119  while ( (un = *iter) ) {
120  un->SetNebula( neb );
121  ++iter;
122  }
123  }
124 }
125 
127 {
128  return active == activeStarSystem;
129 }
130 
131 bool Unit::TransferUnitToSystem( unsigned int whichJumpQueue,
132  class StarSystem* &previouslyActiveStarSystem,
133  bool DoSightAndSound )
134 {
135  return false;
136 }
137 
139 {
140  return computer;
141 }
142 
143 bool Unit::AutoPilotTo( Unit *un, bool automaticenergyrealloc )
144 {
145  std::string tmp;
146  return AutoPilotToErrorMessage( un, automaticenergyrealloc, tmp );
147 }
148 
149 void Unit::SetAfterBurn( float aft )
150 {
151  afterburnenergy = aft;
152 }
153 
154 void Unit::SetFuel( float f )
155 {
156  fuel = f;
157 }
158 
159 void Unit::SetEnergyRecharge( float enrech )
160 {
161  recharge = enrech;
162 }
163 
164 void Unit::SetMaxEnergy( float maxen )
165 {
166  maxenergy = maxen;
167 }
168 
170 {
171  Vector VelocityRef( 0, 0, 0 );
172  {
173  Unit *vr = const_cast< UnitContainer* > (&computer.velocity_ref)->GetUnit();
174  if (vr)
175  VelocityRef = vr->cumulative_velocity;
176  }
177 
178  //return(cumulative_velocity*graphicOptions.WarpFieldStrength);
179  Vector vel = cumulative_velocity-VelocityRef;
180  float speed = vel.Magnitude();
181  //return vel*graphicOptions.WarpFieldStrength;
182  if (speed > 0) {
183  Vector veldir = vel*(1./speed);
184  Vector facing = cumulative_transformation_matrix.getR();
185  float ang = facing.Dot( veldir );
186  float warpfield = graphicOptions.WarpFieldStrength;
187  if (ang < 0) warpfield = 1./warpfield;
188  return ang*facing*speed*(warpfield-1)+vel+VelocityRef;
189  } else {return VelocityRef; }
190 }
191 
192 void Unit::SetPosition( const QVector &pos )
193 {
194  prev_physical_state.position = curr_physical_state.position = pos;
195 }
196 
197 float Unit::DealDamageToHull( const Vector &pnt, float Damage )
198 {
199  float *nullvar = NULL; //short fix
200  return DealDamageToHullReturnArmor( pnt, Damage, nullvar );
201 }
202 
203 void Unit::GetOrientation( Vector &p, Vector &q, Vector &r ) const
204 {
205  Matrix m;
206  curr_physical_state.to_matrix( m );
207  p = m.getP();
208  q = m.getQ();
209  r = m.getR();
210 }
211 
213 {
214  Vector p, q, r;
215  GetOrientation( p, q, r );
216  Vector res( NetLocalForce.i*p+NetLocalForce.j*q+NetLocalForce.k*r );
217  if (NetForce.i || NetForce.j || NetForce.k)
218  res += InvTransformNormal( identity_matrix, NetForce );
219  return res/GetMass();
220 }
221 
222 float Unit::GetMaxAccelerationInDirectionOf( const Vector &ref, bool afterburn ) const
223 {
224  Vector p, q, r;
225  GetOrientation( p, q, r );
226  Vector lref( ref*p, ref*q, ref*r );
227  float tp = (lref.i == 0) ? 0 : fabs( Limits().lateral/lref.i );
228  float tq = (lref.j == 0) ? 0 : fabs( Limits().vertical/lref.j );
229  float tr = (lref.k == 0) ? 0 : fabs( ( (lref.k > 0) ? Limits().forward : Limits().retro )/lref.k );
230  float trqmin = (tr < tq) ? tr : tq;
231  float tm = tp < trqmin ? tp : trqmin;
232  return lref.Magnitude()*tm/GetMass();
233 }
234 
235 void Unit::SetVelocity( const Vector &v )
236 {
237  Velocity = v;
238 }
239 
241 {
242  AngularVelocity = v;
243 }
244 
245 bool Unit::InRange( Unit *target, double &mm, bool cone, bool cap, bool lock ) const
246 {
247  if (this == target || target->CloakVisible() < .8)
248  return false;
249  if (cone && computer.radar.maxcone > -.98) {
250  QVector delta( target->Position()-Position() );
251  mm = delta.Magnitude();
252  if ( (!lock) || ( !(TargetLocked() && computer.target == target) ) ) {
253  double tempmm = mm-target->rSize();
254  if (tempmm > 0.0001)
255  if ( (ToLocalCoordinates( Vector( delta.i, delta.j, delta.k ) ).k/tempmm) < computer.radar.maxcone && cone )
256  return false;
257  }
258  } else {
259  mm = ( target->Position()-Position() ).Magnitude();
260  }
261  //owner==target?!
262  if ( ( ( mm-rSize()-target->rSize() ) > computer.radar.maxrange ) || target->rSize() < computer.radar.mintargetsize ) {
263  Flightgroup *fg = target->getFlightgroup();
264  if ( ( target->rSize() < capship_size || (!cap) ) && (fg == NULL ? true : fg->name != "Base") )
265  return target->isUnit() == PLANETPTR;
266  }
267  return true;
268 }
269 
271 {
272  return computer.target.GetUnit();
273 }
274 
276 {
277  return computer.velocity_ref.GetUnit();
278 }
279 
281 {
282  return computer.threat.GetUnit();
283 }
284 
286 {
288 }
289 
290 void Unit::Ref()
291 {
292 #ifdef CONTAINER_DEBUG
293  CheckUnit( this );
294 #endif
295  ++ucref;
296 }
297 
299 {
300  this->old_state.setPosition( this->curr_physical_state.position );
301  this->old_state.setOrientation( this->curr_physical_state.orientation );
302  this->old_state.setVelocity( this->Velocity );
303  this->old_state.setAcceleration( this->net_accel );
304 }
305 
306 bool isMissile( const weapon_info *weap )
307 {
308  static bool useProjectile =
309  XMLSupport::parse_bool( vs_config->getVariable( "graphics", "hud", "projectile_means_missile", "false" ) );
310  if (useProjectile && weap->type == weapon_info::PROJECTILE)
311  return true;
312  if (useProjectile == false && weap->size >= weapon_info::LIGHTMISSILE)
313  return true;
314  return false;
315 }
316 
317 bool flickerDamage( Unit *un, float hullpercent )
318 {
319 #define damagelevel hullpercent
320  static double counter = getNewTime();
321  static float flickertime = XMLSupport::parse_float( vs_config->getVariable( "graphics", "glowflicker", "time", "30" ) );
322  static float flickerofftime =
323  XMLSupport::parse_float( vs_config->getVariable( "graphics", "glowflicker", "off-time", "2" ) );
324  static float minflickercycle =
325  XMLSupport::parse_float( vs_config->getVariable( "graphics", "glowflicker", "min-cycle", "2" ) );
326  static float flickeronprob =
327  XMLSupport::parse_float( vs_config->getVariable( "graphics", "glowflicker", "num-times-per-second-on", ".66" ) );
328  static float hullfornoflicker =
329  XMLSupport::parse_float( vs_config->getVariable( "graphics", "glowflicker", "hull-for-total-dark", ".04" ) );
330  float diff = getNewTime()-counter;
331  if (diff > flickertime) {
332  counter = getNewTime();
333  diff = 0;
334  }
335  float tmpflicker = flickertime*damagelevel;
336  if (tmpflicker < minflickercycle)
337  tmpflicker = minflickercycle;
338  diff = fmod( diff, tmpflicker );
339  //we know counter is somewhere between 0 and damage level
340  //cast this to an int for fun!
341  unsigned int thus = ( (unsigned int) (size_t) un )>>2;
342  thus = thus%( (unsigned int) tmpflicker );
343  diff = fmod( diff+thus, tmpflicker );
344  if (flickerofftime > diff) {
345  if (damagelevel > hullfornoflicker)
346  return rand() > RAND_MAX * GetElapsedTime()*flickeronprob;
347  else
348  return true;
349  }
350  return false;
351 
352 #undef damagelevel
353 }
354 
355 //SERIOUSLY BROKEN
356 Vector ReflectNormal( const Vector &vel, const Vector &norm )
357 {
358  //THIS ONE WORKS...but no...we don't want works return norm * (2*vel.Dot(norm)) - vel;
359  return norm*vel.Magnitude();
360 }
361 
362 #define INVERSEFORCEDISTANCE 5400
363 extern void abletodock( int dock );
364 
365 bool CrashForceDock( Unit *thus, Unit *dockingUn, bool force )
366 {
367  Unit *un = dockingUn;
368  int whichdockport = thus->CanDockWithMe( un, force );
369  if (whichdockport != -1) {
370  if (Network == NULL) {
371  QVector place = UniverseUtil::SafeEntrancePoint( un->Position(), un->rSize()*1.5 );
372  un->SetPosAndCumPos( place );
373  if (un->ForceDock( thus, whichdockport ) > 0) {
374  abletodock( 3 );
375  un->UpgradeInterface( thus );
376  return true;
377  }
378  } else {
379  int playernum = _Universe->whichPlayerStarship( dockingUn );
380  if (playernum >= 0)
381  Network[playernum].dockRequest( thus->GetSerial() );
382  return false;
383  }
384  }
385  return false;
386 }
387 
389  const QVector &biglocation,
390  const Vector &bignormal,
391  const QVector &smalllocation,
392  const Vector &smallnormal,
393  float dist )
394 {
395  clsptr smltyp = smalle->isUnit();
396  if (smltyp == ENHANCEMENTPTR || smltyp == MISSILEPTR) {
397  if (isUnit() != ENHANCEMENTPTR && isUnit() != MISSILEPTR) {
398  smalle->reactToCollision( this, smalllocation, smallnormal, biglocation, bignormal, dist );
399  return;
400  }
401  }
402  static bool crash_dock_unit = XMLSupport::parse_bool( vs_config->getVariable( "physics", "unit_collision_docks", "false" ) );
403  if (crash_dock_unit) {
404  Unit *dockingun = smalle;
405  Unit *thus = this;
406  if ( _Universe->isPlayerStarship( this ) ) {
407  thus = smalle;
408  dockingun = this;
409  }
410  if ( _Universe->isPlayerStarship( dockingun ) ) {
411  if (UnitUtil::getFlightgroupName( thus ) == "Base") {
412  static bool crash_dock_hangar =
413  XMLSupport::parse_bool( vs_config->getVariable( "physics", "only_hangar_collision_docks", "false" ) );
414  if ( CrashForceDock( thus, smalle, !crash_dock_hangar ) )
415  return;
416  }
417  }
418  }
419  //don't bounce if you can Juuuuuuuuuuuuuump
420  if ( !jumpReactToCollision( smalle ) ) {
421  static float kilojoules_per_damage =
422  XMLSupport::parse_float( vs_config->getVariable( "physics", "kilojoules_per_unit_damage", "5400" ) );
423  static float collision_scale_factor =
424  XMLSupport::parse_float( vs_config->getVariable( "physics", "collision_damage_scale", "1.0" ) );
425  static float inelastic_scale = XMLSupport::parse_float( vs_config->getVariable( "physics", "inelastic_scale", ".8" ) );
426  static float mintime =
427  XMLSupport::parse_float( vs_config->getVariable( "physics", "minimum_time_between_recorded_player_collisions", "0.1" ) );
428  float m1 = smalle->GetMass(), m2 = GetMass();
429  if (m1 < 1e-6f || m2 < 1e-6f) {
430  if (m1 <= 0) m1 = 0.0f;
431  if (m2 <= 0) m2 = 0.0f;
432  m1 += 1.0e-7f;
433  m2 += 1.0e-7f;
434  }
435  //Compute linear velocity of points of impact by taking into account angular velocities
436  Vector small_velocity = smalle->GetVelocity()-smalle->GetAngularVelocity().Cross( smalllocation-smalle->Position() );
437  Vector big_velocity = GetVelocity()-GetAngularVelocity().Cross( biglocation-Position() );
438  //Compute reference frame conversions to align along force normals (newZ)(currently using bignormal
439  // - will experiment to see if both are needed for sufficient approximation)
440  Vector orthoz = ( (m2*bignormal)-(m1*smallnormal) ).Normalize();
441  Vector orthox = MakeNonColinearVector( orthoz );
442  Vector orthoy( 0, 0, 0 );
443  //need z and non-colinear x to compute new basis trio. destroys x,y, preserves z.
444  Orthogonize( orthox, orthoy, orthoz );
445  //transform matrix from normal aligned space
446  Matrix fromNewRef( orthox, orthoy, orthoz );
447  Matrix toNewRef = fromNewRef;
448  //transform matrix to normal aligned space
449  fromNewRef.InvertRotationInto( toNewRef );
450  Vector small_velocity_aligned = Transform( toNewRef, small_velocity );
451  Vector big_velocity_aligned = Transform( toNewRef, big_velocity );
452  //Compute elastic and inelastic terminal velocities (point object approximation)
453  //doesn't need aligning (I think)
454  Vector Inelastic_vf = ( m1/(m1+m2) )*small_velocity+( m2/(m1+m2) )*big_velocity;
455  //compute along aligned dimension, then return to previous reference frame
456  small_velocity_aligned.k = (small_velocity_aligned.k*(m1-m2)/(m1+m2)+( 2.0f*m2/(m1+m2) )*big_velocity_aligned.k);
457  big_velocity_aligned.k = (big_velocity_aligned.k*(m2-m1)/(m1+m2)+( 2.0f*m1/(m1+m2) )*small_velocity_aligned.k);
458  Vector SmallerElastic_vf = Transform( fromNewRef, small_velocity_aligned );
459  Vector ThisElastic_vf = Transform( fromNewRef, big_velocity_aligned );
460  //HACK ALERT:
461  //following code referencing minvel and time between collisions attempts
462  //to alleviate ping-pong problems due to collisions being detected
463  //after the player has penetrated the hull of another vessel because of discretization of time.
464  //this should eventually be replaced by instead figuring out where
465  //the point of collision should have occurred, and moving the vessels to the
466  //actual collision location before applying forces
467  Cockpit *thcp = _Universe->isPlayerStarship( this );
468  Cockpit *smcp = _Universe->isPlayerStarship( smalle );
469  bool isnotplayerorhasbeenmintime = true;
470  //Need to incorporate normals of colliding polygons somehow, without overiding directions of travel.
471  //We'll use the point object approximation for the magnitude of damage, and then apply the force along the appropriate normals
472  //ThisElastic_vf=((ThisElastic_vf.Magnitude()>minvel||!thcp)?ThisElastic_vf.Magnitude():minvel)*smallnormal;
473  //SmallerElastic_vf=((SmallerElastic_vf.Magnitude()>minvel||!smcp)?SmallerElastic_vf.Magnitude():minvel)*bignormal;
474  Vector ThisFinalVelocity = inelastic_scale*Inelastic_vf+(1.0f-inelastic_scale)*ThisElastic_vf;
475  Vector SmallerFinalVelocity = inelastic_scale*Inelastic_vf+(1.0f-inelastic_scale)*SmallerElastic_vf;
476  //float LargeKE = (0.5)*m2*GetVelocity().MagnitudeSquared();
477  //float SmallKE = (0.5)*m1*smalle->GetVelocity().MagnitudeSquared();
478  //float FinalInelasticKE = Inelastic_vf.MagnitudeSquared()*(0.5)*(m1+m2);
479  //float InelasticDeltaKE = LargeKE +SmallKE - FinalInelasticKE;
480  //1/2Mass*deltavfromnoenergyloss^2
481  float LargeDeltaE = (0.5f)*m2*(ThisFinalVelocity-ThisElastic_vf).MagnitudeSquared();
482  //1/2Mass*deltavfromnoenergyloss^2
483  float SmallDeltaE = (0.5f)*m1*(SmallerFinalVelocity-SmallerElastic_vf).MagnitudeSquared();
484  //Damage distribution (NOTE: currently arbitrary - no known good model for calculating how much energy object endures as a result of the collision)
485  float large_damage = (0.25f*SmallDeltaE+0.75f*LargeDeltaE)/kilojoules_per_damage*collision_scale_factor;
486  float small_damage = (0.25f*LargeDeltaE+0.75f*SmallDeltaE)/kilojoules_per_damage*collision_scale_factor;
487  //Vector ThisDesiredVelocity = ThisElastic_vf*(1-inelastic_scale/2)+Inelastic_vf*inelastic_scale/2;
488  //Vector SmallerDesiredVelocity = SmallerElastic_vf*(1-inelastic_scale)+Inelastic_vf*inelastic_scale;
489  //FIXME need to resolve 2 problems -
490  //1) SIMULATION_ATOM for small != SIMULATION_ATOM for large (below smforce line should mostly address this)
491  //2) Double counting due to collision occurring for each object in a different physics frame.
492  Vector smforce =
493  (SmallerFinalVelocity
494  -small_velocity)*smalle->GetMass()
495  /( SIMULATION_ATOM*( (float) smalle->sim_atom_multiplier )/( (float) this->sim_atom_multiplier ) );
496  Vector thisforce = (ThisFinalVelocity-big_velocity)*GetMass()/SIMULATION_ATOM;
497  if (thcp) {
498  if ( (getNewTime()-thcp->TimeOfLastCollision) > mintime )
500  else
501  isnotplayerorhasbeenmintime = false;
502  }
503  if (smcp) {
504  if ( (getNewTime()-smcp->TimeOfLastCollision) > mintime )
506  else
507  isnotplayerorhasbeenmintime = false;
508  }
509  if (Network != NULL) {
510  //Only player units can move in network mode.
511  if (thcp)
512  this->ApplyForce( thisforce-smforce );
513  else if (smcp)
514  smalle->ApplyForce( smforce-thisforce );
515  } else {
516  //Collision force caps primarily for AI-AI collisions. Once the AIs get a real collision avoidance system, we can
517  // turn damage for AI-AI collisions back on, and then we can remove these caps.
518  static float maxTorqueMultiplier =
519  XMLSupport::parse_float( vs_config->getVariable( "physics", "maxCollisionTorqueMultiplier", ".67" ) ); //value, in seconds of desired maximum recovery time
520  static float maxForceMultiplier =
521  XMLSupport::parse_float( vs_config->getVariable( "physics", "maxCollisionForceMultiplier", "5" ) ); //value, in seconds of desired maximum recovery time
522  if ( (smalle->isUnit() != MISSILEPTR) && isnotplayerorhasbeenmintime ) {
523  //for torque... smalllocation -- approximation hack of MR^2 for rotational inertia (moment of inertia currently just M)
524  Vector torque = smforce/(smalle->radial_size*smalle->radial_size);
525  Vector force = smforce-torque;
526 
527  float maxForce = maxForceMultiplier*(smalle->limits.forward+smalle->limits.retro
528  +smalle->limits.lateral+smalle->limits.vertical);
529  float maxTorque = maxTorqueMultiplier*(smalle->limits.yaw
530  +smalle->limits.pitch+smalle->limits.roll);
531  //Convert from frames to seconds, so that the specified value is meaningful
532  maxForce = maxForce/(smalle->sim_atom_multiplier*SIMULATION_ATOM);
533  maxTorque = maxTorque/(smalle->sim_atom_multiplier*SIMULATION_ATOM);
534  float tMag = torque.Magnitude();
535  float fMag = force.Magnitude();
536  if (tMag > maxTorque)
537  torque *= (maxTorque/tMag);
538  if (fMag > maxForce)
539  force *= (maxForce/fMag);
540  smalle->ApplyTorque( torque, smalllocation );
541  smalle->ApplyForce( force-torque );
542  }
543  if ( (this->isUnit() != MISSILEPTR) && isnotplayerorhasbeenmintime ) {
544  //for torque ... biglocation -- approximation hack of MR^2 for rotational inertia
545  Vector torque = thisforce/(radial_size*radial_size);
546  Vector force = thisforce-torque;
547  float maxForce = maxForceMultiplier*(limits.forward+limits.retro
548  +limits.lateral+limits.vertical);
549  float maxTorque = maxTorqueMultiplier*(limits.yaw+limits.pitch+limits.roll);
550  //Convert from frames to seconds, so that the specified value is meaningful
551  maxForce = maxForce/(this->sim_atom_multiplier*SIMULATION_ATOM);
552  maxTorque = maxTorque/(this->sim_atom_multiplier*SIMULATION_ATOM);
553  float tMag = torque.Magnitude();
554  float fMag = force.Magnitude();
555  if (tMag > maxTorque)
556  torque *= (maxTorque/tMag);
557  if (fMag > maxForce)
558  force *= (maxForce/fMag);
559  this->ApplyTorque( torque, biglocation );
560  this->ApplyForce( force-torque );
561  }
562  }
563  static int upgradefac =
564  XMLSupport::parse_bool( vs_config->getVariable( "physics", "cargo_deals_collide_damage",
565  "false" ) ) ? -1 : FactionUtil::GetUpgradeFaction();
566  bool dealdamage = true;
567  if ( _Universe->AccessCamera() ) {
568  Vector smalldelta = ( _Universe->AccessCamera()->GetPosition()-smalle->Position() ).Cast();
569  float smallmag = smalldelta.Magnitude();
570  Vector thisdelta = ( _Universe->AccessCamera()->GetPosition()-this->Position() ).Cast();
571  float thismag = thisdelta.Magnitude();
572  static float collision_hack_distance =
573  XMLSupport::parse_float( vs_config->getVariable( "physics", "collision_avoidance_hack_distance", "10000" ) );
574  static float front_collision_hack_distance =
575  XMLSupport::parse_float( vs_config->getVariable( "physics", "front_collision_avoidance_hack_distance", "200000" ) );
576  if (thcp == NULL && smcp == NULL) {
577  if (smallmag > collision_hack_distance+this->rSize() && thismag > collision_hack_distance) {
578  static float front_collision_hack_angle =
579  cos( 3.1415926536f
581  "front_collision_avoidance_hack_angle",
582  "40" ) )/180.0f );
583  if (smalldelta.Dot( _Universe->AccessCamera()->GetR() ) < smallmag*front_collision_hack_angle
584  && thisdelta.Dot( _Universe->AccessCamera()->GetR() ) < thismag*front_collision_hack_angle) {
585  if (smallmag > front_collision_hack_distance+this->rSize() && thismag > front_collision_hack_distance)
586  dealdamage = false;
587  } else {
588  dealdamage = false;
589  }
590  }
591  }
592  }
593  if ( !_Universe->isPlayerStarship( this ) && !_Universe->isPlayerStarship( smalle ) ) {
594  if (this->isUnit() != MISSILEPTR && smalle->isUnit() != MISSILEPTR) {
595  static bool collisionDamageToAI =
596  XMLSupport::parse_bool( vs_config->getVariable( "physics", "collisionDamageToAI", "false" ) );
597  if (!collisionDamageToAI)
598  //HACK: Stupid AI ships always crash into each other.
599  dealdamage = false;
600  }
601  }
602  if (dealdamage) {
603  if (faction != upgradefac) {
604  smalle->ApplyDamage( biglocation.Cast(), bignormal, small_damage, smalle, GFXColor( 1,
605  1,
606  1,
607  2 ), this->owner
608  != NULL ? this->owner : this );
609  }
610  if (smalle->faction != upgradefac) {
611  this->ApplyDamage( smalllocation.Cast(), smallnormal, large_damage, this, GFXColor( 1,
612  1,
613  1,
614  2 ), smalle->owner
615  != NULL ? smalle->owner : smalle );
616  }
617  }
618  }
619 }
620 
621 void Unit::ActivateJumpDrive( int destination )
622 {
623  if ( ( ( docked&(DOCKED|DOCKED_INSIDE) ) == 0 ) && jump.drive != -2 )
624  jump.drive = destination;
625 }
626 
628 {
629  if (jump.drive >= 0)
630  jump.drive = -1;
631 }
632 
633 float copysign( float x, float y )
634 {
635  if (y > 0)
636  return x;
637  else
638  return -x;
639 }
640 
641 float rand01()
642 {
643  return (float) rand()/(float) RAND_MAX;
644 }
645 
646 float capship_size = 500;
647 
648 /* UGLYNESS short fix */
649 unsigned int apply_float_to_unsigned_int( float tmp )
650 {
651  static unsigned long int seed = 2531011;
652  seed += 214013;
653  seed %= 4294967295u;
654  unsigned int ans = (unsigned int) tmp;
655  tmp -= ans; //now we have decimal;
656  if ( seed < (unsigned long int) (4294967295u*tmp) )
657  ans += 1;
658  return ans;
659 }
660 
661 std::string accelStarHandler( const XMLType &input, void *mythis )
662 {
663  static float game_speed = XMLSupport::parse_float( vs_config->getVariable( "physics", "game_speed", "1" ) );
664  static float game_accel = XMLSupport::parse_float( vs_config->getVariable( "physics", "game_accel", "1" ) );
665  return XMLSupport::tostring( *input.w.f/(game_speed*game_accel) );
666 }
667 
668 std::string speedStarHandler( const XMLType &input, void *mythis )
669 {
670  static float game_speed = XMLSupport::parse_float( vs_config->getVariable( "physics", "game_speed", "1" ) );
671  return XMLSupport::tostring( (*input.w.f)/game_speed );
672 }
673 
674 static list< Unit* >Unitdeletequeue;
676 int deathofvs = 1;
677 void CheckUnit( Unit *un )
678 {
679  if (deletedUn.Get( (long) un ) != NULL)
680  while (deathofvs)
681  printf( "%ld died", (long) un );
682 }
683 
684 void UncheckUnit( Unit *un )
685 {
686  if (deletedUn.Get( (long) un ) != NULL)
687  deletedUn.Delete( (long) un );
688 }
689 
690 string GetUnitDir( string filename )
691 {
692  return filename.substr( 0, filename.find( "." ) );
693 }
694 
695 char * GetUnitDir( const char *filename )
696 {
697  char *retval = strdup( filename );
698  if (retval[0] == '\0')
699  return retval;
700  if (retval[1] == '\0')
701  return retval;
702  for (int i = 0; retval[i] != 0; ++i)
703  if (retval[i] == '.') {
704  retval[i] = '\0';
705  break;
706  }
707  return retval;
708 }
709 
710 //From weapon_xml.cpp
711 std::string lookupMountSize( int s )
712 {
713  std::string result;
714  if (s&weapon_info::LIGHT)
715  result += "LIGHT ";
716  if (s&weapon_info::MEDIUM)
717  result += "MEDIUM ";
718  if (s&weapon_info::HEAVY)
719  result += "HEAVY ";
721  result += "CAPSHIP-LIGHT ";
723  result += "CAPSHIP-HEAVY ";
724  if (s&weapon_info::SPECIAL)
725  result += "SPECIAL ";
727  result += "LIGHT-MISSILE ";
729  result += "MEDIUM-MISSILE ";
731  result += "HEAVY-MISSILE ";
733  result += "LIGHT-CAPSHIP-MISSILE ";
735  result += "HEAVY-CAPSHIP-MISSILE ";
737  result += "SPECIAL-MISSILE ";
739  result += "AUTOTRACKING ";
740  return result;
741 }
742 
743 /*
744  **********************************************************************************
745  **** UNIT STUFF
746  **********************************************************************************
747  */
748 Unit::Unit( int /*dummy*/ ) : cumulative_transformation_matrix( identity_matrix )
749 {
750  ZeroAll();
751  pImage = (new UnitImages< void >);
752  sound = new UnitSounds;
753  aistate = NULL;
754  pImage->cockpit_damage = NULL;
756  Init();
757 }
758 
759 Unit::Unit() : cumulative_transformation_matrix( identity_matrix )
760 {
761  ZeroAll();
762  pImage = (new UnitImages< void >);
763  sound = new UnitSounds;
764  aistate = NULL;
765  pImage->cockpit_damage = NULL;
767  Init();
768 }
769 
770 Unit::Unit( std::vector< Mesh* > &meshes, bool SubU, int fact ) : cumulative_transformation_matrix( identity_matrix )
771 {
772  ZeroAll();
773  pImage = (new UnitImages< void >);
774  sound = new UnitSounds;
775  pilot = new Pilot( fact );
776  aistate = NULL;
777  pImage->cockpit_damage = NULL;
778  Init();
779  hull = 1000;
780  maxhull = 100000;
781  this->faction = fact;
782  graphicOptions.SubUnit = SubU;
783  meshdata = meshes;
784  meshes.clear();
785  meshdata.push_back( NULL );
786  calculate_extent( false );
787  pilot->SetComm( this );
788 }
789 
790 extern void update_ani_cache();
791 Unit::Unit( const char *filename,
792  bool SubU,
793  int faction,
794  std::string unitModifications,
795  Flightgroup *flightgrp,
796  int fg_subnumber,
797  string *netxml ) : cumulative_transformation_matrix( identity_matrix )
798 {
799  ZeroAll();
800  pImage = (new UnitImages< void >);
801  sound = new UnitSounds;
802  pilot = new Pilot( faction );
803  aistate = NULL;
804  pImage->cockpit_damage = NULL;
805  Init( filename, SubU, faction, unitModifications, flightgrp, fg_subnumber, netxml );
806  pilot->SetComm( this );
807 }
808 
810 {
811  if(pMeshAnimation) {
812  delete pMeshAnimation;
813  pMeshAnimation = NULL;
814  }
815 
816  free( pImage->cockpit_damage );
817  if ( (!killed) )
818  VSFileSystem::vs_fprintf( stderr, "Assumed exit on unit %s(if not quitting, report error)\n", name.get().c_str() );
819  if (ucref)
820  VSFileSystem::vs_fprintf( stderr, "DISASTER AREA!!!!" );
821 #ifdef DESTRUCTDEBUG
822  VSFileSystem::vs_fprintf( stderr, "stage %d %x %d\n", 0, this, ucref );
823  fflush( stderr );
824 #endif
825 #ifdef DESTRUCTDEBUG
826  VSFileSystem::vs_fprintf( stderr, "%d %x ", 1, planet );
827  fflush( stderr );
828  VSFileSystem::vs_fprintf( stderr, "%d %x\n", 2, pImage->pHudImage );
829  fflush( stderr );
830 #endif
831  if (pImage->unitwriter)
832  delete pImage->unitwriter;
833  delete pImage;
834 #ifdef DESTRUCTDEBUG
835  VSFileSystem::vs_fprintf( stderr, "%d %x", 3, pImage );
836  fflush( stderr );
837 #endif
838  delete sound;
839  delete pilot;
840 #ifdef DESTRUCTDEBUG
841  VSFileSystem::vs_fprintf( stderr, "%d", 5 );
842  fflush( stderr );
843 #endif
844 #ifdef DESTRUCTDEBUG
845  VSFileSystem::vs_fprintf( stderr, "%d %x", 6, &mounts );
846  fflush( stderr );
847 #endif
848 
849 #ifdef DESTRUCTDEBUG
850  VSFileSystem::vs_fprintf( stderr, "%d %x ", 9, halos );
851  fflush( stderr );
852 #endif
853 #ifdef DESTRUCTDEBUG
854  VSFileSystem::vs_fprintf( stderr, "%d %x ", 1, &mounts );
855  fflush( stderr );
856 #endif
857 #ifndef NO_MOUNT_STAR
858  for (vector< Mount* >::iterator jj = mounts.begin(); jj != mounts.end(); ++jj)
859  //Free all mounts elements
860  if ( (*jj) != NULL )
861  delete (*jj);
862 #endif
863  mounts.clear();
864 #ifdef DESTRUCTDEBUG
865  VSFileSystem::vs_fprintf( stderr, "%d", 0 );
866  fflush( stderr );
867 #endif
868  for (unsigned int meshcount = 0; meshcount < meshdata.size(); ++meshcount)
869  if (meshdata[meshcount])
870  delete meshdata[meshcount];
871  meshdata.clear();
872 }
873 
874 void Unit::ZeroAll()
875 {
876  sound = NULL;
877  ucref = 0;
878  networked = false;
879  serial = 0;
880  net_accel.i = 0;
881  net_accel.j = 0;
882  net_accel.k = 0;
883  SavedAccel.i = 0;
884  SavedAccel.j = 0;
885  SavedAccel.k = 0;
886  //old_state has a constructor
887  damages = NO_DAMAGE;
888  //SubUnits has a constructor
890  nebula = NULL;
891  activeStarSystem = NULL;
892  //computer has a constructor
893  //jump needs fixing
894  selected = false;
895  //scanner needs fixing
896  xml = NULL;
897  owner = NULL;
898  //prev_physical_state has a constructor
899  //curr_physical_state has a constructor
900  //cumulative_transformation_matrix has a constructor
901  //cumulative_transformation has a constructor
902  cumulative_velocity.i = 0;
903  cumulative_velocity.j = 0;
904  cumulative_velocity.k = 0;
905  NetForce.i = 0;
906  NetForce.j = 0;
907  NetForce.k = 0;
908  NetLocalForce.i = 0;
909  NetLocalForce.j = 0;
910  NetLocalForce.k = 0;
911  NetTorque.i = 0;
912  NetTorque.j = 0;
913  NetTorque.k = 0;
914  NetLocalTorque.i = 0;
915  NetLocalTorque.j = 0;
916  NetLocalTorque.k = 0;
917  AngularVelocity.i = 0;
918  AngularVelocity.j = 0;
919  AngularVelocity.k = 0;
920  Velocity.i = 0;
921  Velocity.j = 0;
922  Velocity.k = 0;
923  pImage = NULL;
924  Mass = 0;
925  shieldtight = 0; //this can be used to differentiate whether this is a capship or a fighter?
926  fuel = 0;
927  afterburnenergy = 0;
928  afterburntype = 0;
929  Momentofinertia = 0;
930  //limits has a constructor
931  cloaking = 0;
932  cloakmin = 0;
933  radial_size = 0;
934  killed = false;
935  invisible = 0;
936  corner_min.i = 0;
937  corner_min.j = 0;
938  corner_min.k = 0;
939  corner_max.i = 0;
940  corner_max.j = 0;
941  corner_max.k = 0;
942  resolveforces = false;
943  //armor has a constructor
944  //shield has a constructor
945  hull = 0;
946  maxhull = 0;
947  recharge = 0;
948  maxenergy = 0;
949  energy = 0;
950  maxwarpenergy = 0;
951  warpenergy = 0;
952  //target_fgid has a constructor
953  aistate = NULL;
954  //CollideInfo has a constructor
955  colTrees = NULL;
956  docked = NOT_DOCKED;
957  faction = 0;
958  flightgroup = NULL;
961 
962  pMeshAnimation = NULL;
963 }
964 
966 {
968  for (unsigned int locind = 0; locind < NUM_COLLIDE_MAPS; ++locind)
969  set_null( location[locind] );
970  specInterdiction = 0;
972  predicted_priority = 1;
974  last_processed_sqs = 0;
975  do_subunit_scheduling = false;
976  if (Network == NULL)
977  this->networked = 0;
978  else
979  this->networked = 1;
980  damages = NO_DAMAGE;
981 
984  inertialmode = false;
985  turretstatus = 0;
986  autopilotactive = false;
987  this->unit_role = this->attack_preference = ROLES::getRole( "INERT" );
988  this->computer.combat_mode = true;
989 #ifdef CONTAINER_DEBUG
990  UncheckUnit( this );
991 #endif
992  static float capsize = XMLSupport::parse_float( vs_config->getVariable( "physics", "capship_size", "500" ) );
993 
994  capship_size = capsize;
995  activeStarSystem = NULL;
996  xml = NULL;
997  docked = NOT_DOCKED;
999  jump.energy = 100;
1000  static float insys_jump_cost = XMLSupport::parse_float( vs_config->getVariable( "physics", "insystem_jump_cost", ".1" ) );
1001  jump.insysenergy = insys_jump_cost*jump.energy;
1002  jump.delay = 5;
1003  jump.damage = 0;
1004  jump.warpDriveRating = 0;
1005  graphicOptions.FaceCamera = false;
1006  jump.drive = -2; //disabled
1007  afterburnenergy = 0;
1008  nebula = NULL;
1009  limits.structurelimits = Vector( 0, 0, 1 );
1010  limits.limitmin = -1;
1011  cloaking = -1;
1012  pImage->repair_droid = 0;
1013  pImage->next_repair_time = -FLT_MAX;
1014  pImage->next_repair_cargo = ~0;
1015  pImage->ecm = 0;
1016  pImage->cloakglass = false;
1017  pImage->CargoVolume = 0;
1018  pImage->UpgradeVolume = 0;
1019  this->HeatSink = 0;
1020 
1021  pImage->unitwriter = NULL;
1022  cloakmin = pImage->cloakglass ? 1 : 0;
1023  pImage->equipment_volume = 0;
1025  pImage->cloakrate = 100;
1026  pImage->cloakenergy = 0;
1027  pImage->forcejump = false;
1028  sound->engine = -1;
1029  sound->armor = -1;
1030  sound->shield = -1;
1031  sound->hull = -1;
1032  sound->explode = -1;
1033  sound->cloak = -1;
1034  sound->jump = -1;
1039  pImage->CommFunctionality = 1.0f;
1040  pImage->CommFunctionalityMax = 1.0f;
1043 
1044  pImage->pHudImage = NULL;
1045 
1046  //Freedom for the masses!!!! //you'll have to justify why setting to this is better.
1047  owner = NULL;
1048  faction = 0;
1049  resolveforces = true;
1050  colTrees = NULL;
1052  corner_min.Set( FLT_MAX, FLT_MAX, FLT_MAX );
1053  corner_max.Set( -FLT_MAX, -FLT_MAX, -FLT_MAX );
1054 
1055  //BUCO! Must add shield tightness back into units.csv for great justice.
1056  static float default_shield_tightness =
1057  XMLSupport::parse_float( vs_config->getVariable( "physics", "default_shield_tightness", "0" ) );
1058  //was 0 // sphere mesh by default, but let's decide on it
1059  shieldtight =
1060  default_shield_tightness;
1061  energy = maxenergy = 1;
1062  warpenergy =
1063  0;
1064  maxwarpenergy =
1065  0;
1066  recharge = 1;
1067  shield.recharge =
1068  shield.leak = 0;
1069  this->shield.efficiency = 1;
1070  shield.shield2fb.front = shield.shield2fb.back = shield.shield2fb.frontmax = shield.shield2fb.backmax =
1074  armor.backlefttop =
1076  armor
1077  .
1078  backrightbottom
1079  =
1080  armor.
1081  frontleftbottom
1082  =
1083  armor
1084  .
1085  backleftbottom
1086  =
1087  0;
1088  hull = 1; //10;
1089  maxhull = 1; //10;
1090  shield.number =
1091  0;
1092 
1093  pImage->pExplosion = NULL;
1094  pImage->timeexplode = 0;
1095  killed = false;
1096  ucref = 0;
1097  aistate = NULL;
1101  Mass = .01;
1102  fuel = 000;
1103 
1104  static Vector myang( XMLSupport::parse_float( vs_config->getVariable( "general", "pitch", "0" ) ), XMLSupport::parse_float(
1105  vs_config->getVariable( "general", "yaw", "0" ) ), XMLSupport::parse_float( vs_config->getVariable(
1106  "general",
1107  "roll",
1108  "0" ) ) );
1109  static float rr = XMLSupport::parse_float( vs_config->getVariable( "graphics", "hud", "radarRange", "20000" ) );
1110  static float minTrackingNum = XMLSupport::parse_float( vs_config->getVariable( "physics",
1111  "autotracking",
1112  ".93" ) ); //DO NOT CHANGE see unit_customize.cpp
1113 
1114  //DO NOT CHANGE see unit_customize.cpp
1115  static float lc = XMLSupport::parse_float( vs_config->getVariable( "physics", "lock_cone", ".8" ) );
1116 
1117  Momentofinertia = .01;
1118  AngularVelocity = myang;
1119  cumulative_velocity = Velocity = Vector( 0, 0, 0 );
1120 
1121  NetTorque = NetLocalTorque = Vector( 0, 0, 0 );
1122  NetForce = Vector( 0, 0, 0 );
1123  NetLocalForce = Vector( 0, 0, 0 );
1124 
1125  selected = false;
1126 
1127  limits.yaw = 2.55;
1128  limits.pitch = 2.55;
1129  limits.roll = 2.55;
1130 
1131  limits.lateral = 2;
1132  limits.vertical = 8;
1133  limits.forward = 2;
1134  limits.afterburn = 5;
1135  limits.retro = 2;
1136  VelocityReference( NULL );
1137  computer.threat.SetUnit( NULL );
1138  computer.threatlevel = 0;
1140  computer.set_speed = 0;
1144 
1147  computer.NavPoint = Vector( 0, 0, 0 );
1148  computer.itts = false;
1149  computer.radar.maxrange = rr;
1150  computer.radar.locked = false;
1151  computer.radar.maxcone = -1;
1152  computer.radar.trackingcone = minTrackingNum;
1154  computer.radar.lockcone = lc;
1157  computer.ecmactive = true;
1158 
1159  flightgroup = NULL;
1161  //No cockpit reference here
1162  if (!pImage->cockpit_damage) {
1163  unsigned int numg = (1+MAXVDUS+UnitImages< void >::NUMGAUGES)*2;
1164  pImage->cockpit_damage = (float*) malloc( (numg)*sizeof (float) );
1165  for (unsigned int damageiterator = 0; damageiterator < numg; ++damageiterator)
1166  pImage->cockpit_damage[damageiterator] = 1;
1167  }
1168 }
1169 
1170 std::string getMasterPartListUnitName();
1171 using namespace VSFileSystem;
1172 extern std::string GetReadPlayerSaveGame( int );
1173 CSVRow GetUnitRow( string filename, bool subu, int faction, bool readLast, bool &read );
1174 #if 0
1175 static std::string csvUnit( std::string un )
1176 {
1177  string::size_type i = un.find_last_of( "." );
1178  string::size_type del = un.find_last_of( "/\\:" );
1179  if (i == std::string::npos)
1180  return un+".csv";
1181  if (del == std::string::npos || del < i)
1182  return un.substr( 0, i )+".csv";
1183  return un+".csv";
1184 }
1185 #endif
1186 void Unit::Init( const char *filename,
1187  bool SubU,
1188  int faction,
1189  std::string unitModifications,
1190  Flightgroup *flightgrp,
1191  int fg_subnumber,
1192  string *netxml )
1193 {
1194  static bool UNITTAB = XMLSupport::parse_bool( vs_config->getVariable( "physics", "UnitTable", "false" ) );
1195  CSVRow unitRow;
1196  this->Unit::Init();
1197  graphicOptions.SubUnit = SubU ? 1 : 0;
1198  graphicOptions.Animating = 1;
1199  graphicOptions.RecurseIntoSubUnitsOnCollision = !isSubUnit();
1200  this->faction = faction;
1201  SetFg( flightgrp, fg_subnumber );
1202  VSFile f;
1203  VSFile f2;
1204  VSError err = Unspecified;
1205  VSFile unitTab;
1206  VSError taberr = Unspecified;
1207  bool foundFile = false;
1208  if (netxml == NULL) {
1209  if (unitModifications.length() != 0) {
1210  string nonautosave = GetReadPlayerSaveGame( _Universe->CurrentCockpit() );
1211  string filepath( "" );
1212  //In network mode we only look in the save subdir in HOME
1213  if (Network == NULL && !SERVER) {
1214  if ( nonautosave.empty() ) {
1216  filepath = unitModifications+"/"+string( filename );
1217  } else {
1219  filepath = nonautosave+"/"+string( filename );
1220  }
1221  }
1222  //Try to open save
1223  if (filename[0]) {
1224  taberr = unitTab.OpenReadOnly( filepath+".csv", UnitSaveFile );
1225  if (taberr <= Ok) {
1226  unitTables.push_back( new CSVTable( unitTab, unitTables.back()->rootdir ) );
1227  unitTab.Close();
1228  }
1229  if (!UNITTAB)
1230  err = f.OpenReadOnly( filepath, UnitSaveFile );
1231  }
1232  }
1233  }
1234  if (netxml)
1235  unitTables.push_back( new CSVTable( *netxml, unitTables.back()->rootdir ) );
1236  //If save was not succesfull we try to open the unit file itself
1237  if (netxml == NULL) {
1238  if (filename[0]) {
1239  string subdir = "factions/"+FactionUtil::GetFactionName( faction );
1240  //begin deprecated code (5/11)
1241  if (UNITTAB) {} else {
1242  if (err > Ok) {
1243  f.SetSubDirectory( subdir );
1244  //No save found loading default unit
1245  err = f.OpenReadOnly( filename, UnitFile );
1246  if (err > Ok) {
1247  f.SetSubDirectory( "" );
1248  err = f.OpenReadOnly( filename, UnitFile );
1249  }
1250  } else {
1251  f2.SetSubDirectory( subdir );
1252  //Save found so just opening default unit to get its directory for further loading
1253  err = f2.OpenReadOnly( filename, UnitFile );
1254  if (err > Ok) {
1255  f2.SetSubDirectory( "" );
1256  err = f2.OpenReadOnly( filename, UnitFile );
1257  }
1258  }
1259  }
1260  //end deprecated code
1261  }
1262  }
1263  if (UNITTAB)
1264  unitRow = GetUnitRow( filename, SubU, faction, true, foundFile );
1265  else
1266  foundFile = (err <= Ok);
1267  this->filename = filename;
1268  if (!foundFile) {
1269  bool istemplate = ( string::npos != ( string( filename ).find( ".template" ) ) );
1270  static bool usingtemplates = XMLSupport::parse_bool( vs_config->getVariable( "data", "usingtemplates", "true" ) );
1271  if ( !istemplate || (istemplate && usingtemplates) )
1272  cout<<"Unit file "<<filename<<" not found"<<endl;
1273  meshdata.clear();
1274  meshdata.push_back( NULL );
1275  this->fullname = filename;
1276  this->name = string( "LOAD_FAILED" );
1277  calculate_extent( false );
1278  radial_size = 1;
1279  if ( (taberr <= Ok && taberr != Unspecified) || netxml ) {
1280  delete unitTables.back();
1281  unitTables.pop_back();
1282  }
1283  pilot->SetComm( this );
1284  return;
1285  }
1286  this->name = this->filename;
1287  bool tmpbool;
1288  if (UNITTAB) {
1289  //load from table?
1290 
1291  //we have to set the root directory to where the saved unit would have come from.
1292  //saved only exists if taberr<=Ok && taberr!=Unspecified...that's why we pass in said boolean
1293  VSFileSystem::current_path.push_back( taberr <= Ok && taberr
1294  != Unspecified ? GetUnitRow( filename, SubU, faction, false,
1295  tmpbool ).getRoot() : unitRow.getRoot() );
1296  VSFileSystem::current_subdirectory.push_back( "/"+unitRow["Directory"] );
1297  VSFileSystem::current_type.push_back( UnitFile );
1298  LoadRow( unitRow, unitModifications, netxml );
1299  VSFileSystem::current_type.pop_back();
1301  VSFileSystem::current_path.pop_back();
1302  if ( (taberr <= Ok && taberr != Unspecified) || netxml ) {
1303  delete unitTables.back();
1304  unitTables.pop_back();
1305  }
1306  } else {
1307  if (netxml == NULL)
1308  Unit::LoadXML( f, unitModifications.c_str() );
1309  else
1310  Unit::LoadXML( f, "", netxml );
1311  if (err <= Ok)
1312  f.Close();
1313  if ( f2.Valid() )
1314  f2.Close();
1315  }
1316  calculate_extent( false );
1317  pilot->SetComm( this );
1318 
1319  this->pMeshAnimation = new MeshAnimation(this);
1320  bool initsucc = pMeshAnimation->Init(filename, faction, flightgrp);
1321  if(initsucc) {
1322  pMeshAnimation->SetAniSpeed( 0.05 );
1323  pMeshAnimation->StartAnimation();
1324  } else {
1325  delete pMeshAnimation;
1326  pMeshAnimation = NULL;
1327  }
1328 }
1329 
1330 vector< Mesh* >Unit::StealMeshes()
1331 {
1332  vector< Mesh* >ret;
1333 
1334  Mesh *shield = meshdata.empty() ? NULL : meshdata.back();
1335  for (int i = 0; i < nummesh(); ++i)
1336  ret.push_back( meshdata[i] );
1337  meshdata.clear();
1338  meshdata.push_back( shield );
1339 
1340  return ret;
1341 }
1342 
1343 static float tmpmax( float a, float b )
1344 {
1345  return a > b ? a : b;
1346 }
1347 
1348 bool CheckAccessory( Unit *tur )
1349 {
1350  bool accessory = tur->name.get().find( "accessory" ) != string::npos;
1351  if (accessory) {
1354  tur->GetComputerData().max_roll_right ) ) );
1355  }
1356  return accessory;
1357 }
1358 
1359 void Unit::calculate_extent( bool update_collide_queue )
1360 {
1361  int a;
1362  corner_min = Vector( FLT_MAX, FLT_MAX, FLT_MAX );
1363  corner_max = Vector( -FLT_MAX, -FLT_MAX, -FLT_MAX );
1364  for (a = 0; a < nummesh(); ++a) {
1365  corner_min = corner_min.Min( meshdata[a]->corner_min() );
1366  corner_max = corner_max.Max( meshdata[a]->corner_max() );
1367  } /* have subunits now in table*/
1368  const Unit *un;
1369  for (un_kiter iter = SubUnits.constIterator(); (un = *iter); ++iter) {
1370  corner_min = corner_min.Min( un->LocalPosition().Cast()+un->corner_min );
1371  corner_max = corner_max.Max( un->LocalPosition().Cast()+un->corner_max );
1372  }
1373  if ( corner_min.i == FLT_MAX || corner_max.i == -FLT_MAX || !FINITE( corner_min.i ) || !FINITE( corner_max.i ) ) {
1374  radial_size = 0;
1375  corner_min.Set( 0, 0, 0 );
1376  corner_max.Set( 0, 0, 0 );
1377  } else {
1378  float tmp1 = corner_min.Magnitude();
1379  float tmp2 = corner_max.Magnitude();
1380  radial_size = tmp1 > tmp2 ? tmp1 : tmp2;
1381  }
1382  if ( !isSubUnit() && update_collide_queue && (maxhull > 0) ) {
1383  //only do it in Unit::CollideAll UpdateCollideQueue();
1384  }
1385  if (isUnit() == PLANETPTR)
1386  radial_size = tmpmax( tmpmax( corner_max.i, corner_max.j ), corner_max.k );
1387 }
1388 
1390 {
1391  if (activeStarSystem) {
1392  return activeStarSystem;
1393  } else {
1394  Cockpit *cp = _Universe->isPlayerStarship( this );
1395  if (cp)
1396  if (cp->activeStarSystem)
1397  return cp->activeStarSystem;
1398  }
1399  return _Universe->activeStarSystem();
1400 }
1401 
1403 {
1404  if (activeStarSystem) {
1405  return activeStarSystem;
1406  } else {
1407  Cockpit *cp = _Universe->isPlayerStarship( this );
1408  if (cp)
1409  if (cp->activeStarSystem)
1410  return cp->activeStarSystem;
1411  }
1412  return _Universe->activeStarSystem();
1413 }
1414 
1416 {
1417  static bool
1418  client_side_fire = XMLSupport::parse_bool( vs_config->getVariable( "network", "client_side_fire", "true" ) );
1419  return client_side_fire && wi->type != weapon_info::BEAM && wi->type != weapon_info::PROJECTILE;
1420 }
1421 
1422 void Unit::Fire( unsigned int weapon_type_bitmask, bool listen_to_owner )
1423 {
1424  static bool can_fire_in_spec = XMLSupport::parse_bool( vs_config->getVariable( "physics", "can_fire_in_spec", "false" ) );
1425  static bool can_fire_in_cloak = XMLSupport::parse_bool( vs_config->getVariable( "physics", "can_fire_in_cloak", "false" ) );
1426  static bool verbose_debug = XMLSupport::parse_bool( vs_config->getVariable( "data", "verbose_debug", "false" ) );
1427  if ( (cloaking >= 0 && can_fire_in_cloak == false) || (graphicOptions.InWarp && can_fire_in_spec == false) ) {
1428  UnFire();
1429  return;
1430  }
1431  unsigned int mountssize = mounts.size();
1432  int playernum = _Universe->whichPlayerStarship( this );
1433  vector< int >gunFireRequests;
1434  vector< int >missileFireRequests;
1435  vector< int >serverUnfireRequests;
1436  for (unsigned int counter = 0; counter < mountssize; ++counter) {
1437  unsigned int index = counter;
1438  Mount *i = &mounts[index];
1439  if (i->status != Mount::ACTIVE)
1440  continue;
1441  if (i->bank == true) {
1442  unsigned int best = index;
1443  unsigned int j;
1444  for (j = index+1; j < mountssize; ++j) {
1445  if ( i->NextMountCloser( &mounts[j], this ) ) {
1446  best = j;
1447  if ( SERVER && (mounts[j].processed == Mount::FIRED || mounts[j].processed == Mount::PROCESSED) )
1448  serverUnfireRequests.push_back( j );
1449  i->UnFire();
1450  i = &mounts[j];
1451  } else {
1452  if ( SERVER && (mounts[j].processed == Mount::FIRED || mounts[j].processed == Mount::PROCESSED) )
1453  serverUnfireRequests.push_back( j );
1454  mounts[j].UnFire();
1455  }
1456  if (mounts[j].bank == false) {
1457  ++j;
1458  break;
1459  }
1460  }
1461  counter = j-1; //will increment to the next one
1462  index = best;
1463  }
1464  const bool mis = isMissile( i->type );
1465  const bool locked_on = i->time_to_lock <= 0;
1466  const bool lockable_weapon = i->type->LockTime > 0;
1467  const bool autotracking_gun = (!mis) && 0 != (i->size&weapon_info::AUTOTRACKING) && locked_on;
1468  const bool fire_non_autotrackers = ( 0 == (weapon_type_bitmask&ROLES::FIRE_ONLY_AUTOTRACKERS) );
1469  const bool locked_missile = (mis && locked_on && lockable_weapon);
1470  const bool missile_and_want_to_fire_missiles = ( mis && (weapon_type_bitmask&ROLES::FIRE_MISSILES) );
1471  const bool gun_and_want_to_fire_guns = ( (!mis) && (weapon_type_bitmask&ROLES::FIRE_GUNS) );
1472  if (verbose_debug && missile_and_want_to_fire_missiles && locked_missile)
1473  VSFileSystem::vs_fprintf( stderr, "\n about to fire locked missile \n" );
1474  bool want_to_fire =
1475  (fire_non_autotrackers || autotracking_gun || locked_missile)
1476  && ( (ROLES::EVERYTHING_ELSE&weapon_type_bitmask&i->type->role_bits) || i->type->role_bits == 0 )
1477  && ( (locked_on && missile_and_want_to_fire_missiles) || gun_and_want_to_fire_guns );
1478  if ( (*i).type->type == weapon_info::BEAM ) {
1479  if ( (*i).type->EnergyRate*SIMULATION_ATOM > energy ) {
1480  //On server side send a PACKET TO ALL CLIENT TO NOTIFY UNFIRE
1481  //Including the one who fires to make sure it stops
1482  if ( SERVER && ( (*i).processed == Mount::FIRED || (*i).processed == Mount::PROCESSED ) )
1483  serverUnfireRequests.push_back( index );
1484  //NOT ONLY IN non-networking mode : anyway, the server will tell everyone including us to stop if not already done
1485  (*i).UnFire();
1486  continue;
1487  }
1488  } else
1489  //Only in non-networking mode
1490  if (i->type->EnergyRate > energy) {
1491  if (!want_to_fire) {
1492  if ( SERVER && ( (*i).processed == Mount::FIRED || (*i).processed == Mount::PROCESSED ) )
1493  serverUnfireRequests.push_back( index );
1494  i->UnFire();
1495  }
1496  if (Network == NULL)
1497  continue;
1498  }
1499  if (want_to_fire) {
1500  //If in non-networking mode and mount fire has been accepted or if on server side
1501  if (Network != NULL && (!SERVER) && i->processed != Mount::ACCEPTED && i->processed != Mount::FIRED
1502  && i->processed != Mount::REQUESTED && playernum >= 0 && i->ammo != 0) {
1503  //Request a fire order to the server telling him the serial of the unit and the mount index (nm)
1504  if (mis)
1505  missileFireRequests.push_back( index );
1506  else
1507  gunFireRequests.push_back( index );
1508  //Mark the mount as fire requested
1510  //NETFIXME: REQUESTED was commented out.
1511  }
1512  //projectile and beam weapons should be confirmed by server...not just fired off willy-nilly
1513  if ( Network == NULL || SERVER || i->processed == Mount::ACCEPTED || preEmptiveClientFire( i->type ) ) {
1514  //If we are on server or if the weapon has been accepted for fire we fire
1515  if ( i->Fire( this, owner == NULL ? this : owner, mis, listen_to_owner ) ) {
1516  ObjSerial serid;
1517  if (missile_and_want_to_fire_missiles) {
1518  serid = getUniqueSerial();
1519  i->serial = serid;
1520  } else {
1521  serid = 0;
1522  }
1523  if (SERVER) {
1524  if (serid) {
1525  //One Serial ID per broadcast. Not mush point in optimizing this.
1526  vector< int >indexvec;
1527  indexvec.push_back( index );
1528  VSServer->BroadcastFire( this->serial, indexvec, serid, this->energy,
1529  this->getStarSystem()->GetZone() );
1530  } else {
1531  gunFireRequests.push_back( index );
1532  }
1533  }
1534  //We could only refresh energy on server side or in non-networking mode, on client side it is done with
1535  //info the server sends with ack for fire
1536  //FOR NOW WE TRUST THE CLIENT SINCE THE SERVER CAN REFUSE A FIRE
1537  //if( Network==NULL || SERVER)
1538  if (i->type->type == weapon_info::BEAM) {
1539  if (i->ref.gun)
1540  if ( ( !i->ref.gun->Dissolved() ) || i->ref.gun->Ready() )
1541  energy -= i->type->EnergyRate*SIMULATION_ATOM;
1542  } else if ( isMissile( i->type ) ) {
1543  energy -= i->type->EnergyRate;
1544  }
1545  //IF WE REFRESH ENERGY FROM SERVER : Think to send the energy update to the firing client with ACK TO fireRequest
1546  //fire only 1 missile at a time
1547  if (mis) weapon_type_bitmask &= (~ROLES::FIRE_MISSILES);
1548  }
1549  }
1550  }
1551  if ( want_to_fire == false
1553  i->UnFire();
1554  if (SERVER)
1555  serverUnfireRequests.push_back( index );
1556  }
1557  }
1558  if ( !gunFireRequests.empty() ) {
1559  if (SERVER) {
1560  VSServer->BroadcastFire( this->serial, gunFireRequests, 0, this->energy, this->getStarSystem()->GetZone() );
1561  } else {
1562  char mis2 = false;
1563  Network[playernum].fireRequest( this->serial, gunFireRequests, mis2 );
1564  }
1565  }
1566  if ( SERVER && !serverUnfireRequests.empty() )
1567  VSServer->BroadcastUnfire( this->serial, serverUnfireRequests, this->getStarSystem()->GetZone() );
1568  //Client missile requests can be grouped because clients only send a boolean, not a serial.
1569  if ( !SERVER && !missileFireRequests.empty() ) {
1570  char mis2 = true;
1571  Network[playernum].fireRequest( this->serial, missileFireRequests, mis2 );
1572  }
1573 }
1574 
1575 const string Unit::getFgID()
1576 {
1577  if (flightgroup != NULL) {
1578  char buffer[32];
1579  sprintf( buffer, "-%d", flightgroup_subnumber );
1580  return flightgroup->name+buffer;
1581  } else {
1582  return fullname;
1583  }
1584 }
1585 
1586 void Unit::SetFaction( int faction )
1587 {
1588  this->faction = faction;
1589  for (un_iter ui = getSubUnits(); (*ui) != NULL; ++ui)
1590  (*ui)->SetFaction( faction );
1591 }
1592 
1593 //FIXME Daughter units should be able to be turrets (have y/p/r)
1594 void Unit::SetResolveForces( bool ys )
1595 {
1596  resolveforces = ys;
1597 }
1598 
1599 void Unit::SetFg( Flightgroup *fg, int fg_subnumber )
1600 {
1601  flightgroup = fg;
1602  flightgroup_subnumber = fg_subnumber;
1603 }
1604 
1605 void Unit::AddDestination( const std::string &dest )
1606 {
1607  pImage->destination.push_back( dest );
1608 }
1609 
1610 const std::vector< std::string >& Unit::GetDestinations() const
1611 {
1612  return pImage->destination;
1613 }
1614 
1615 float Unit::TrackingGuns( bool &missilelock )
1616 {
1617  float trackingcone = 0;
1618  missilelock = false;
1619  for (int i = 0; i < GetNumMounts(); ++i) {
1620  if ( mounts[i].status == Mount::ACTIVE && (mounts[i].size&weapon_info::AUTOTRACKING) )
1621  trackingcone = computer.radar.trackingcone;
1622  if (mounts[i].status == Mount::ACTIVE && mounts[i].type->LockTime > 0 && mounts[i].time_to_lock <= 0)
1623  missilelock = true;
1624  }
1625  return trackingcone;
1626 }
1627 
1629 {
1630  float mrange = -1;
1631  float grange = -1;
1632  float speed = -1;
1633  bool beam = true;
1634  if ( GetNumMounts() ) {
1635  grange = 0;
1636  speed = 0;
1637  mrange = 0;
1638  int nummt = 0;
1639  //this breaks the name, but... it _is_ more useful.
1640  for (int i = 0; i < GetNumMounts(); ++i)
1641  if (mounts[i].status == Mount::ACTIVE || mounts[i].status == Mount::INACTIVE) {
1642  if (isMissile( mounts[i].type ) == false) {
1643  if (mounts[i].type->Range > grange)
1644  grange = mounts[i].type->Range;
1645  if (mounts[i].status == Mount::ACTIVE) {
1646  speed += mounts[i].type->Speed;
1647  ++nummt;
1648  beam &= (mounts[i].type->type == weapon_info::BEAM);
1649  }
1650  } else if ( isMissile( mounts[i].type ) ) {
1651  if (mounts[i].type->Range > mrange)
1652  mrange = mounts[i].type->Range;
1653  }
1654  }
1655  if (nummt) {
1656  if (beam)
1657  speed = FLT_MAX;
1658  else
1659  speed = speed/nummt;
1660  }
1661  }
1662  this->missilerange = mrange;
1663  this->gunrange = grange;
1664  this->gunspeed = speed;
1665 }
1666 
1667 QVector Unit::PositionITTS( const QVector &absposit, Vector velocity, float speed, bool steady_itts ) const
1668 {
1669  if (speed == FLT_MAX)
1670  return this->Position();
1671  float difficultyscale = 1;
1672  if (g_game.difficulty < .99)
1673  GetVelocityDifficultyMult( difficultyscale );
1674  velocity = (cumulative_velocity.Scale( difficultyscale )-velocity);
1675  QVector posit( this->Position()-absposit );
1676  QVector curguess( posit );
1677  for (unsigned int i = 0; i < 3; ++i) {
1678  float time = 0;
1679  if (speed > 0.001)
1680  time = curguess.Magnitude()/speed;
1681  if (steady_itts)
1682  //** jay
1683  curguess = posit+GetVelocity().Cast().Scale( time );
1684  else
1685  curguess = posit+velocity.Scale( time ).Cast();
1686  }
1687  return curguess+absposit;
1688 }
1689 
1690 static float tmpsqr( float x )
1691 {
1692  return x*x;
1693 }
1694 
1695 float CloseEnoughCone( Unit *me )
1696 {
1697  static float close_autotrack_cone = XMLSupport::parse_float( vs_config->getVariable( "physics", "near_autotrack_cone", ".9" ) );
1698  return close_autotrack_cone;
1699 }
1700 
1701 bool CloseEnoughToAutotrack( Unit *me, Unit *targ, float &cone )
1702 {
1703  if (targ) {
1704  static float close_enough_to_autotrack =
1705  tmpsqr( XMLSupport::parse_float( vs_config->getVariable( "physics", "close_enough_to_autotrack", "4" ) ) );
1706  float dissqr = ( me->curr_physical_state.position.Cast()-targ->curr_physical_state.position.Cast() ).MagnitudeSquared();
1707  float movesqr = close_enough_to_autotrack
1708  *( me->prev_physical_state.position.Cast()-me->curr_physical_state.position.Cast() ).MagnitudeSquared();
1709  if (dissqr < movesqr && movesqr > 0) {
1710  cone = CloseEnoughCone( me )*(movesqr-dissqr)/movesqr+1*dissqr/movesqr;
1711  return true;
1712  }
1713  }
1714  return false;
1715 }
1716 
1717 //Caps at +/- 1 so as to account for floating point inaccuracies.
1718 static inline float safeacos( float mycos )
1719 {
1720  if (mycos > 1.)
1721  mycos = 1.;
1722  if (mycos < -1.)
1723  mycos = -1;
1724  return acos( mycos );
1725 }
1726 
1727 float Unit::cosAngleTo( Unit *targ, float &dist, float speed, float range, bool turnmargin ) const
1728 {
1729  Vector Normal( cumulative_transformation_matrix.getR() );
1730  Normalize( Normal );
1731  QVector totarget( targ->PositionITTS( cumulative_transformation.position, cumulative_velocity, speed, false ) );
1732  totarget = totarget-cumulative_transformation.position;
1733  dist = totarget.Magnitude();
1734 
1735  //Trial code
1736  float turnlimit =
1737  tmpmax( tmpmax( computer.max_yaw_left, computer.max_yaw_right ), tmpmax( computer.max_pitch_up, computer.max_pitch_down ) );
1738  float turnangle = SIMULATION_ATOM
1739  *tmpmax( turnlimit,
1740  tmpmax( SIMULATION_ATOM*.5*(limits.yaw+limits.pitch),
1741  sqrtf( AngularVelocity.i*AngularVelocity.i+AngularVelocity.j*AngularVelocity.j ) ) );
1742  float ittsangle = safeacos( Normal.Cast().Dot( totarget.Scale( 1./totarget.Magnitude() ) ) );
1743  QVector edgeLocation = (targ->cumulative_transformation_matrix.getP()*targ->rSize()+totarget);
1744  float radangle = safeacos( edgeLocation.Cast().Scale( 1./edgeLocation.Magnitude() ).Dot( totarget.Normalize() ) );
1745  float rv = ittsangle-radangle-(turnmargin ? turnangle : 0);
1746 
1747  float rsize = targ->rSize()+rSize();
1748  if ( (!targ->GetDestinations().empty() && jump.drive >= 0) || (targ->faction == faction) )
1749  rsize = 0; //HACK so missions work well
1750  if (range != 0)
1751  dist = (dist-rsize)/range;
1752 
1753  else
1754  dist = 0;
1755  if (!FINITE( dist ) || dist < 0)
1756  dist = 0;
1757  return (rv < 0) ? 1 : cos( rv );
1758 }
1759 
1760 float Unit::cosAngleFromMountTo( Unit *targ, float &dist ) const
1761 {
1762  float retval = -1;
1763  dist = FLT_MAX;
1764  float tmpcos;
1765  Matrix mat;
1766  for (int i = 0; i < GetNumMounts(); ++i) {
1767  float tmpdist = .001;
1768  Transformation finaltrans( mounts[i].GetMountOrientation(), mounts[i].GetMountLocation().Cast() );
1769  finaltrans.Compose( cumulative_transformation, cumulative_transformation_matrix );
1770  finaltrans.to_matrix( mat );
1771  Vector Normal( mat.getR() );
1772 
1773  QVector totarget( targ->PositionITTS( finaltrans.position, cumulative_velocity, mounts[i].type->Speed, false ) );
1774 
1775  tmpcos = Normal.Dot( totarget.Cast() );
1776  tmpdist = totarget.Magnitude();
1777  if (tmpcos > 0) {
1778  tmpcos = tmpdist*tmpdist-tmpcos*tmpcos;
1779  //one over distance perpendicular away from straight ahead times the size...high is good WARNING POTENTIAL DIV/0
1780  tmpcos = targ->rSize()/tmpcos;
1781  } else {
1782  tmpcos /= tmpdist;
1783  }
1784  //UNLIKELY DIV/0
1785  tmpdist /= mounts[i].type->Range;
1786  if (tmpdist < 1 || tmpdist < dist) {
1787  if (tmpcos-tmpdist/2 > retval-dist/2) {
1788  dist = tmpdist;
1789  retval = tmpcos;
1790  }
1791  }
1792  }
1793  return retval;
1794 }
1795 
1796 #define PARANOIA (0.4f)
1797 
1798 void Unit::Threaten( Unit *targ, float danger )
1799 {
1800  if (!targ) {
1801  computer.threatlevel = danger;
1802  computer.threat.SetUnit( NULL );
1803  } else if (targ->owner != this && this->owner != targ && danger > PARANOIA && danger > computer.threatlevel) {
1804  computer.threat.SetUnit( targ );
1805  computer.threatlevel = danger;
1806  }
1807 }
1808 
1809 const std::string& Unit::getCockpit() const
1810 {
1811  return pImage->cockpitImage.get();
1812 }
1813 
1815 {
1816  selected = true;
1817 }
1818 
1820 {
1821  selected = false;
1822 }
1823 
1824 void disableSubUnits( Unit *uhn )
1825 {
1826  Unit *un;
1827  for (un_iter i = uhn->getSubUnits(); (un = *i) != NULL; ++i)
1828  disableSubUnits( un );
1829  for (unsigned int j = 0; j < uhn->mounts.size(); ++j)
1830  DestroyMount( &uhn->mounts[j] );
1831 }
1832 
1834 {
1835  return SubUnits.createIterator();
1836 }
1837 
1839 {
1840  return SubUnits.constIterator();
1841 }
1842 
1843 void Unit::SetVisible( bool vis )
1844 {
1845  if (vis)
1846  invisible &= (~INVISCAMERA);
1847  else
1848  invisible |= INVISCAMERA;
1849 }
1850 
1851 void Unit::SetAllVisible( bool vis )
1852 {
1853  if (vis)
1854  invisible &= (~INVISUNIT);
1855  else
1856  invisible |= INVISUNIT;
1857 }
1858 
1859 void Unit::SetGlowVisible( bool vis )
1860 {
1861  if (vis)
1862  invisible &= (~INVISGLOW);
1863  else
1864  invisible |= INVISGLOW;
1865 }
1866 
1868 {
1869  return .5;
1870 }
1871 
1872 /*
1873  **********************************************************************************
1874  **** UNIT AI STUFF
1875  **********************************************************************************
1876  */
1877 void Unit::LoadAIScript( const std::string &s )
1878 {
1879  if (s.find( ".py" ) != string::npos) {
1881  PrimeOrders( ai );
1882  return;
1883  } else {
1884  if (s.length() > 0) {
1885  if (*s.begin() == '_') {
1886  mission->addModule( s.substr( 1 ) );
1887  PrimeOrders( new AImissionScript( s.substr( 1 ) ) );
1888  } else {
1889  if (s == "ikarus") {
1890  PrimeOrders( new Orders::Ikarus() );
1891  } else {
1892  string ai_agg = s+".agg.xml";
1893  PrimeOrders( new Orders::AggressiveAI( ai_agg.c_str() ) );
1894  }
1895  }
1896  } else {
1897  PrimeOrders();
1898  }
1899  }
1900 }
1901 
1902 void Unit::eraseOrderType( unsigned int type )
1903 {
1904  if (aistate)
1905  aistate->eraseType( type );
1906 }
1907 
1909 {
1911  if (pyai) {
1912  PrimeOrders( pyai );
1913  } else if (!aistate) {
1914  PrimeOrders();
1915  return false;
1916  }
1917  return true;
1918 }
1919 
1921 {
1923  if (pyai)
1924  EnqueueAI( pyai );
1925  else if (!aistate)
1926  return false;
1927  return true;
1928 }
1929 
1930 void Unit::PrimeOrders( Order *newAI )
1931 {
1932  if (newAI) {
1933  if (aistate)
1934  aistate->Destroy();
1935  aistate = newAI;
1936  newAI->SetParent( this );
1937  } else {
1938  PrimeOrders();
1939  }
1940 }
1941 
1943 {
1944  if (aistate) {
1945  aistate->Destroy();
1946  aistate = NULL;
1947  }
1948  aistate = new Order; //get 'er ready for enqueueing
1949  aistate->SetParent( this );
1950 }
1951 
1953 {
1954  if (aistate) {
1955  aistate->Destroy();
1956  aistate = NULL;
1957  }
1958  Vector vec( 0, 0, 10000 );
1959  aistate = new ExecuteFor( new Orders::MatchVelocity( this->ClampVelocity( vec, true ), Vector( 0,
1960  0,
1961  0 ), true, true,
1962  false ), 4.0f );
1963  aistate->SetParent( this );
1964 }
1965 
1966 void Unit::SetAI( Order *newAI )
1967 {
1968  newAI->SetParent( this );
1969  if (aistate)
1970  aistate->ReplaceOrder( newAI );
1971  else
1972  aistate = newAI;
1973 }
1974 
1975 void Unit::EnqueueAI( Order *newAI )
1976 {
1977  newAI->SetParent( this );
1978  if (aistate)
1979  aistate->EnqueueOrder( newAI );
1980  else
1981  aistate = newAI;
1982 }
1983 
1985 {
1986  newAI->SetParent( this );
1987  if (aistate)
1988  aistate->EnqueueOrderFirst( newAI );
1989  else
1990  aistate = newAI;
1991 }
1992 
1994 {
1995  if (flightgroup) {
1996  Unit *leader = flightgroup->leader.GetUnit();
1997  //no heirarchy in flight group
1998  if (leader ? (flightgroup->leader_decision > -1) && ( leader->getFgSubnumber() >= getFgSubnumber() ) : true) {
1999  if (!leader)
2000  flightgroup->leader_decision = flightgroup->nr_ships;
2001  flightgroup->leader.SetUnit( this );
2002  }
2003  flightgroup->leader_decision--;
2004  }
2005  if (aistate) aistate->Execute();
2006  if ( !SubUnits.empty() ) {
2007  un_iter iter = getSubUnits();
2008  Unit *un;
2009  while ( (un = *iter) ) {
2010  un->ExecuteAI(); //like dubya
2011  ++iter;
2012  }
2013  }
2014 }
2015 
2017 {
2018  if ( getAIState() )
2019  return getFgID()+":"+getAIState()->createFullOrderDescription( 0 ).c_str();
2020  else
2021  return "no order";
2022 }
2023 
2024 void Unit::getAverageGunSpeed( float &speed, float &range, float &mmrange ) const
2025 {
2026  speed = gunspeed;
2027  range = gunrange;
2028  mmrange = missilerange;
2029 }
2030 
2031 float Unit::getRelation( const Unit *targ ) const
2032 {
2033  return pilot->GetEffectiveRelationship( this, targ );
2034 }
2035 
2036 void Unit::setTargetFg( string primary, string secondary, string tertiary )
2037 {
2038  target_fgid[0] = primary;
2039  target_fgid[1] = secondary;
2040  target_fgid[2] = tertiary;
2041 
2042  ReTargetFg( 0 );
2043 }
2044 
2045 void Unit::ReTargetFg( int which_target )
2046 {
2047 #if 0
2048  StarSystem *ssystem = _Universe->activeStarSystem();
2049  UnitCollection *unitlist = ssystem->getUnitList();
2050  un_iter uiter = unitlist->createIterator();
2051 
2052  GameUnit *found_target = NULL;
2053  int found_attackers = 1000;
2054  for (GameUnit *other_unit = NULL; other_unit = *uiter; ++uiter) {
2055  string other_fgid = other_unit->getFgID();
2056  if ( other_unit->matchesFg( target_fgid[which_target] ) ) {
2057  //the other unit matches our primary target
2058 
2059  int num_attackers = other_unit->getNumAttackers();
2060  if (num_attackers < found_attackers) {
2061  //there's less ships attacking this target than the previous one
2062  found_target = other_unit;
2063  found_attackers = num_attackers;
2064  setTarget( found_target );
2065  }
2066  }
2067  }
2068  if (found_target == NULL) {
2069  //we haven't found a target yet, search again
2070  if (which_target <= 1)
2071  ReTargetFg( which_target+1 );
2072  else
2073  //we can't find any target
2074  setTarget( NULL );
2075  }
2076 #endif
2077 }
2078 
2080 {
2081  turretstatus = 2;
2082  static bool talkinturrets = XMLSupport::parse_bool( vs_config->getVariable( "AI", "independent_turrets", "false" ) );
2083  if (talkinturrets) {
2084  Unit *un;
2085  for (un_iter iter = getSubUnits(); (un = *iter); ++iter) {
2086  if ( !CheckAccessory( un ) ) {
2087  un->EnqueueAIFirst( new Orders::FireAt( 15.0f ) );
2088  un->EnqueueAIFirst( new Orders::FaceTarget( false, 3 ) );
2089  }
2090  un->SetTurretAI();
2091  }
2092  } else {
2093  Unit *un;
2094  for (un_iter iter = getSubUnits(); (un = *iter); ++iter) {
2095  if ( !CheckAccessory( un ) ) {
2096  if (un->aistate)
2097  un->aistate->Destroy();
2098  un->aistate = ( new Orders::TurretAI() );
2099  un->aistate->SetParent( un );
2100  }
2101  un->SetTurretAI();
2102  }
2103  }
2104 }
2105 
2107 {
2108  turretstatus = 1;
2109  Unit *un;
2110  for (un_iter iter = getSubUnits(); (un = *iter); ++iter) {
2111  if (un->aistate)
2112  un->aistate->Destroy();
2113  un->aistate = new Order; //get 'er ready for enqueueing
2114  un->aistate->SetParent( un );
2115  un->UnFire();
2116  un->DisableTurretAI();
2117  }
2118 }
2119 
2120 /*
2121  **********************************************************************************
2122  **** UNIT_PHYSICS STUFF
2123  **********************************************************************************
2124  */
2125 
2126 extern signed char ComputeAutoGuarantee( Unit *un );
2127 extern float getAutoRSize( Unit *orig, Unit *un, bool ignore_friend = false );
2128 extern void SetShieldZero( Unit* );
2130 {
2131  static float tmp = XMLSupport::parse_float( vs_config->getVariable( "physics", "distance_to_warp", "1000000000000.0" ) );
2132  return tmp;
2133 }
2134 
2135 QVector SystemLocation( std::string system )
2136 {
2137  string xyz = _Universe->getGalaxyProperty( system, "xyz" );
2138  QVector pos;
2139  if ( xyz.size() && (sscanf( xyz.c_str(), "%lf %lf %lf", &pos.i, &pos.j, &pos.k ) >= 3) )
2140  return pos;
2141  else
2142  return QVector( 0, 0, 0 );
2143 }
2144 
2145 static std::string NearestSystem( std::string currentsystem, QVector pos )
2146 {
2147  if (pos.i == 0 && pos.j == 0 && pos.k == 0)
2148  return "";
2149  QVector posnorm = pos.Normalize();
2150  posnorm.Normalize();
2151  QVector cur = SystemLocation( currentsystem );
2152  if (cur.i == 0 && cur.j == 0 && cur.k == 0)
2153  return "";
2154  double closest_distance = 0.0;
2155  std::string closest_system;
2157  GalaxyXML::SubHeirarchy *sectors = &gal->getHeirarchy();
2158  vsUMap< std::string, class GalaxyXML::SGalaxy >::iterator j, i = sectors->begin();
2159  for (; i != sectors->end(); ++i) {
2160  GalaxyXML::SubHeirarchy *systems = &i->second.getHeirarchy();
2161  for (j = systems->begin(); j != systems->end(); ++j) {
2162  std::string place = j->second["xyz"];
2163  if ( place.length() ) {
2164  QVector pos2 = QVector( 0, 0, 0 );
2165  sscanf( place.c_str(), "%lf %lf %lf", &pos2.i, &pos2.j, &pos2.k );
2166  if ( (pos2.i != 0 || pos2.j != 0 || pos2.k != 0) && (pos2.i != cur.i || pos2.j != cur.j || pos2.k != cur.k) ) {
2167  QVector dir = pos2-cur;
2168  QVector norm = dir;
2169  norm.Normalize();
2170  double test = posnorm.Dot( norm );
2171  if (test > .2) {
2172  //test=1-test;
2173  double tmp = dir.MagnitudeSquared()/test/test/test;
2174  for (unsigned int cp = 0; cp < _Universe->numPlayers(); ++cp) {
2175  std::string whereto = _Universe->AccessCockpit( cp )->GetNavSelectedSystem();
2176  if ( whereto.length() == 1+i->first.length()+j->first.length() ) {
2177  if (whereto.substr( 0,
2178  i->first.length() ) == i->first && whereto.substr( i->first.length()+1 )
2179  == j->first) {
2180  static float SystemWarpTargetBonus =
2182  "target_distance_to_warp_bonus",
2183  "1.33" ) );
2184  tmp /= SystemWarpTargetBonus;
2185  }
2186  }
2187  }
2188  if (tmp < closest_distance || closest_distance == 0) {
2189  closest_distance = tmp;
2190  closest_system = i->first+"/"+j->first;
2191  }
2192  }
2193  }
2194  }
2195  }
2196  }
2197  return closest_system;
2198 }
2199 
2201  const Matrix &transmat,
2202  const Vector &cum_vel,
2203  bool lastframe,
2204  UnitCollection *uc,
2205  Unit *superunit )
2206 {
2207  static float VELOCITY_MAX = XMLSupport::parse_float( vs_config->getVariable( "physics", "velocity_max", "10000" ) );
2208  static float SPACE_DRAG = XMLSupport::parse_float( vs_config->getVariable( "physics", "unit_space_drag", "0.000000" ) );
2209  static float EXTRA_CARGO_SPACE_DRAG =
2210  XMLSupport::parse_float( vs_config->getVariable( "physics", "extra_space_drag_for_cargo", "0.005" ) );
2211 
2212  //Save information about when this happened
2213  unsigned int cur_sim_frame = _Universe->activeStarSystem()->getCurrentSimFrame();
2214  //Well, wasn't skipped actually, but...
2215  this->last_processed_sqs = cur_sim_frame;
2216  this->cur_sim_queue_slot = (cur_sim_frame+this->sim_atom_multiplier)%SIM_QUEUE_SIZE;
2217  if (maxhull < 0) {
2218  this->Explode( true, 0 );
2219  }
2220  Transformation old_physical_state = curr_physical_state;
2221  if (docked&DOCKING_UNITS)
2222  PerformDockingOperations();
2223  Repair();
2224  if (fuel < 0)
2225  fuel = 0;
2226  if (cloaking >= cloakmin) {
2227  static bool warp_energy_for_cloak =
2228  XMLSupport::parse_bool( vs_config->getVariable( "physics", "warp_energy_for_cloak", "true" ) );
2229  if ( pImage->cloakenergy*SIMULATION_ATOM > (warp_energy_for_cloak ? warpenergy : energy) ) {
2230  Cloak( false ); //Decloak
2231  } else {
2232  SetShieldZero( this );
2233  if (pImage->cloakrate > 0 || cloaking == cloakmin) {
2234  if (warp_energy_for_cloak)
2235  warpenergy -= (SIMULATION_ATOM*pImage->cloakenergy);
2236  else
2237  energy -= (SIMULATION_ATOM*pImage->cloakenergy);
2238  }
2239  if (cloaking > cloakmin) {
2240  AUDAdjustSound( sound->cloak, cumulative_transformation.position, cumulative_velocity );
2241  //short fix
2242  if ( (cloaking == (2147483647)
2243  && pImage->cloakrate > 0) || (cloaking == cloakmin+1 && pImage->cloakrate < 0) )
2244  AUDStartPlaying( sound->cloak );
2245  //short fix
2246  cloaking -= (int) (pImage->cloakrate*SIMULATION_ATOM);
2247  if (cloaking <= cloakmin && pImage->cloakrate > 0)
2248  cloaking = cloakmin;
2249  if (cloaking < 0 && pImage->cloakrate < 0) {
2250  cloaking = -2147483647-1;
2251  }
2252  }
2253  }
2254  }
2255  //Only on server or non-networking
2256  //Do it everywhere -- "interpolation" for client-side.
2257  //if( SERVER || Network==NULL)
2258  RegenShields();
2259  if (lastframe) {
2260  if ( !( docked&(DOCKED|DOCKED_INSIDE) ) )
2261  //the AIscript should take care
2262  prev_physical_state = curr_physical_state;
2263 #ifdef FIX_TERRAIN
2264  if (planet) {
2265  if (!planet->dirty)
2266  SetPlanetOrbitData( NULL );
2267  else
2268  planet->pps = planet->cps;
2269  }
2270 #endif
2271  }
2272  if (resolveforces) {
2273  //clamp velocity
2274  net_accel = ResolveForces( trans, transmat );
2275  if (Velocity.i > VELOCITY_MAX)
2276  Velocity.i = VELOCITY_MAX;
2277 
2278  else if (Velocity.i < -VELOCITY_MAX)
2279  Velocity.i = -VELOCITY_MAX;
2280  if (Velocity.j > VELOCITY_MAX)
2281  Velocity.j = VELOCITY_MAX;
2282 
2283  else if (Velocity.j < -VELOCITY_MAX)
2284  Velocity.j = -VELOCITY_MAX;
2285  if (Velocity.k > VELOCITY_MAX)
2286  Velocity.k = VELOCITY_MAX;
2287 
2288  else if (Velocity.k < -VELOCITY_MAX)
2289  Velocity.k = -VELOCITY_MAX;
2290  }
2291  float difficulty;
2292  Cockpit *player_cockpit = GetVelocityDifficultyMult( difficulty );
2293 
2294  this->UpdatePhysics2( trans, old_physical_state, net_accel, difficulty, transmat, cum_vel, lastframe, uc );
2295  if (EXTRA_CARGO_SPACE_DRAG > 0) {
2296  int upgfac = FactionUtil::GetUpgradeFaction();
2297  if ( (this->faction == upgfac) || (this->name == "eject") || (this->name == "Pilot") )
2298  Velocity = Velocity*(1-EXTRA_CARGO_SPACE_DRAG);
2299  }
2300  if (SPACE_DRAG > 0)
2301  Velocity = Velocity*(1-SPACE_DRAG);
2302  float dist_sqr_to_target = FLT_MAX;
2303  Unit *target = Unit::Target();
2304  bool increase_locking = false;
2305  if (target && cloaking < 0 /*-1 or -32768*/) {
2306  if (target->isUnit() != PLANETPTR) {
2307  Vector TargetPos( InvTransform( cumulative_transformation_matrix, ( target->Position() ) ).Cast() );
2308  dist_sqr_to_target = TargetPos.MagnitudeSquared();
2309  TargetPos.Normalize();
2310  if (TargetPos.k > computer.radar.lockcone)
2311  increase_locking = true;
2312  }
2313  /* Update the velocity reference to the nearer significant unit/planet. */
2314  if (!computer.force_velocity_ref && activeStarSystem) {
2315  Unit *nextVelRef = activeStarSystem->nextSignificantUnit();
2316  if (nextVelRef) {
2317  if ( computer.velocity_ref.GetUnit() ) {
2318  double dist = UnitUtil::getSignificantDistance( this, computer.velocity_ref.GetUnit() );
2319  double next_dist = UnitUtil::getSignificantDistance( this, nextVelRef );
2320  if (next_dist < dist)
2321  computer.velocity_ref = nextVelRef;
2322  } else {
2323  computer.velocity_ref = nextVelRef;
2324  }
2325  }
2326  }
2327  }
2328  static string LockingSoundName = vs_config->getVariable( "unitaudio", "locking", "locking.wav" );
2329  //enables spiffy wc2 torpedo music, default to normal though
2330  static string LockingSoundTorpName = vs_config->getVariable( "unitaudio", "locking_torp", "locking.wav" );
2331  static int LockingSound = AUDCreateSoundWAV( LockingSoundName, true );
2332  static int LockingSoundTorp = AUDCreateSoundWAV( LockingSoundTorpName, true );
2333 
2334  bool locking = false;
2335  bool touched = false;
2336  for (int i = 0; (int) i < GetNumMounts(); ++i) {
2337  if ( ( (SERVER
2338  && mounts[i].status
2339  == Mount::INACTIVE) || mounts[i].status == Mount::ACTIVE ) && cloaking < 0 && mounts[i].ammo != 0 ) {
2340  if (player_cockpit)
2341  touched = true;
2342  if ( increase_locking && (dist_sqr_to_target < mounts[i].type->Range*mounts[i].type->Range) ) {
2343  mounts[i].time_to_lock -= SIMULATION_ATOM;
2344  static bool ai_lock_cheat = XMLSupport::parse_bool( vs_config->getVariable( "physics", "ai_lock_cheat", "true" ) );
2345  if (!player_cockpit) {
2346  if (ai_lock_cheat)
2347  mounts[i].time_to_lock = -1;
2348  } else {
2349  int LockingPlay = LockingSound;
2350 
2351  //enables spiffy wc2 torpedo music, default to normal though
2352  static bool LockTrumpsMusic =
2353  XMLSupport::parse_bool( vs_config->getVariable( "unitaudio", "locking_trumps_music", "false" ) );
2354  //enables spiffy wc2 torpedo music, default to normal though
2355  static bool TorpLockTrumpsMusic =
2356  XMLSupport::parse_bool( vs_config->getVariable( "unitaudio", "locking_torp_trumps_music", "false" ) );
2357  if (mounts[i].type->LockTime > 0) {
2358  static string LockedSoundName = vs_config->getVariable( "unitaudio", "locked", "locked.wav" );
2359  static int LockedSound = AUDCreateSoundWAV( LockedSoundName, false );
2360  if (mounts[i].type->size == weapon_info::SPECIALMISSILE)
2361  LockingPlay = LockingSoundTorp;
2362 
2363  else
2364  LockingPlay = LockingSound;
2365  if (mounts[i].time_to_lock > -SIMULATION_ATOM && mounts[i].time_to_lock <= 0) {
2366  if ( !AUDIsPlaying( LockedSound ) ) {
2367  UniverseUtil::musicMute( false );
2368  AUDStartPlaying( LockedSound );
2369  AUDStopPlaying( LockingSound );
2370  AUDStopPlaying( LockingSoundTorp );
2371  }
2372  AUDAdjustSound( LockedSound, Position(), GetVelocity() );
2373  } else if (mounts[i].time_to_lock > 0) {
2374  locking = true;
2375  if ( !AUDIsPlaying( LockingPlay ) ) {
2376  if (LockingPlay == LockingSoundTorp)
2377  UniverseUtil::musicMute( TorpLockTrumpsMusic );
2378 
2379  else
2380  UniverseUtil::musicMute( LockTrumpsMusic );
2381  AUDStartPlaying( LockingSound );
2382  }
2383  AUDAdjustSound( LockingSound, Position(), GetVelocity() );
2384  }
2385  }
2386  }
2387  } else if (mounts[i].ammo != 0) {
2388  mounts[i].time_to_lock = mounts[i].type->LockTime;
2389  }
2390  } else if (mounts[i].ammo != 0) {
2391  mounts[i].time_to_lock = mounts[i].type->LockTime;
2392  }
2393  if (mounts[i].type->type == weapon_info::BEAM) {
2394  if (mounts[i].ref.gun) {
2395  static bool must_lock_to_autotrack =
2396  XMLSupport::parse_bool( vs_config->getVariable( "physics", "must_lock_to_autotrack", "true" ) );
2397  Unit *autotarg =
2398  ( (mounts[i].size&weapon_info::AUTOTRACKING)
2399  && (mounts[i].time_to_lock <= 0)
2400  && (player_cockpit == NULL || TargetLocked() || !must_lock_to_autotrack)
2401  && (computer.radar.trackingactive) ) ? target : NULL;
2402  float trackingcone = computer.radar.trackingcone;
2403  if ( CloseEnoughToAutotrack( this, target, trackingcone ) ) {
2404  if (autotarg)
2405  if (computer.radar.trackingcone < trackingcone)
2406  trackingcone = computer.radar.trackingcone;
2407  autotarg = target;
2408  }
2409  mounts[i].ref.gun->UpdatePhysics( cumulative_transformation,
2410  cumulative_transformation_matrix,
2411  autotarg,
2412  trackingcone,
2413  target,
2414  (HeatSink ? HeatSink : 1.0f)*mounts[i].functionality,
2415  this,
2416  superunit );
2417  }
2418  } else {
2419  mounts[i].ref.refire += SIMULATION_ATOM*(HeatSink ? HeatSink : 1.0f)*mounts[i].functionality;
2420  }
2421  if (mounts[i].processed == Mount::FIRED) {
2423  Matrix m1;
2424  t1 = prev_physical_state; //a hack that will not work on turrets
2425  t1.Compose( trans, transmat );
2426  t1.to_matrix( m1 );
2427  int autotrack = 0;
2428  static bool must_lock_to_autotrack =
2429  XMLSupport::parse_bool( vs_config->getVariable( "physics", "must_lock_to_autotrack", "true" ) );
2430  if ( ( 0 != (mounts[i].size&weapon_info::AUTOTRACKING) )
2431  && computer.radar.trackingactive
2432  && ( (Network != NULL && !SERVER)
2433  || player_cockpit == NULL
2434  || TargetLocked()
2435  || !must_lock_to_autotrack ) )
2436  autotrack = computer.itts ? 2 : 1;
2437  float trackingcone = computer.radar.trackingcone;
2438  if ( CloseEnoughToAutotrack( this, target, trackingcone ) ) {
2439  if (autotrack)
2440  if (trackingcone > computer.radar.trackingcone)
2441  trackingcone = computer.radar.trackingcone;
2442  autotrack = 2;
2443  }
2445  for (unsigned int locind = 0; locind < Unit::NUM_COLLIDE_MAPS; ++locind)
2446  hint[locind] =
2447  ( !is_null( superunit->location[locind] ) ) ? superunit->location[locind] : _Universe->activeStarSystem()->
2448  collidemap[locind]->begin();
2449  if ( !mounts[i].PhysicsAlignedFire( this, t1, m1, cumulative_velocity,
2450  (!isSubUnit() || owner == NULL) ? this : owner, target, autotrack,
2451  trackingcone,
2452  hint ) ) {
2453  const weapon_info *typ = mounts[i].type;
2454  energy += typ->EnergyRate*(typ->type == weapon_info::BEAM ? SIMULATION_ATOM : 1);
2455  }
2456  } else if ( mounts[i].processed == Mount::UNFIRED || mounts[i].ref.refire > 2*mounts[i].type->Refire() ) {
2457  mounts[i].processed = Mount::UNFIRED;
2458  mounts[i].PhysicsAlignedUnfire();
2459  }
2460  }
2461  if (locking == false && touched == true) {
2462  if ( AUDIsPlaying( LockingSound ) ) {
2463  UniverseUtil::musicMute( false );
2464  AUDStopPlaying( LockingSound );
2465  }
2466  if ( AUDIsPlaying( LockingSoundTorp ) ) {
2467  UniverseUtil::musicMute( false );
2468  AUDStopPlaying( LockingSoundTorp );
2469  }
2470  }
2471  bool dead = true;
2472 
2473  UpdateSubunitPhysics( cumulative_transformation,
2474  cumulative_transformation_matrix,
2475  cumulative_velocity,
2476  lastframe,
2477  uc,
2478  superunit );
2479  //can a unit get to another system without jumping?.
2480  static bool warp_is_interstellar =
2481  XMLSupport::parse_bool( vs_config->getVariable( "physics", "warp_is_interstellar", "false" ) );
2482  if ( warp_is_interstellar
2483  && ( curr_physical_state.position.MagnitudeSquared() > howFarToJump()*howFarToJump() && !isSubUnit() ) ) {
2484  static bool direct = XMLSupport::parse_bool( vs_config->getVariable( "physics", "direct_interstellar_journey", "true" ) );
2485  bool jumpDirect = false;
2486  if (direct) {
2487  Cockpit *cp = _Universe->isPlayerStarship( this );
2488  if (NULL != cp) {
2489  std::string sys = cp->GetNavSelectedSystem();
2490  if ( !sys.empty() ) {
2491  jumpDirect = true;
2492  _Universe->activeStarSystem()->JumpTo( this, NULL, sys, true, true );
2493  }
2494  }
2495  }
2496  if (!jumpDirect) {
2497  _Universe->activeStarSystem()->JumpTo( this, NULL,
2499  curr_physical_state.position ), true, true );
2500  }
2501  }
2502 //Really kill the unit only in non-networking or on server side
2503  if (hull < 0) {
2504  dead &= (pImage->pExplosion == NULL);
2505  if (dead)
2506  Kill();
2507  } else
2508  if ( !isSubUnit() ) {
2509  for (unsigned int locind = 0; locind < Unit::NUM_COLLIDE_MAPS; ++locind) {
2510  if ( is_null( this->location[locind] ) )
2511  this->getStarSystem()->collidemap[locind]->insert( Collidable( this ) );
2512  else if (locind == Unit::UNIT_BOLT)
2513  //that update will propagate with the flatten
2514  this->getStarSystem()->collidemap[Unit::UNIT_BOLT]->changeKey( this->location[locind], Collidable( this ) );
2515  }
2516  }
2517 }
2518 
2520  const Matrix &transmat,
2521  const Vector &cum_vel,
2522  bool lastframe,
2523  UnitCollection *uc,
2524  Unit *superunit )
2525 {
2526  if ( !SubUnits.empty() ) {
2527  Unit *su;
2528  float backup = SIMULATION_ATOM;
2529  float basesimatom = (this->sim_atom_multiplier ? backup/(float) this->sim_atom_multiplier : backup);
2530  unsigned int cur_sim_frame = _Universe->activeStarSystem()->getCurrentSimFrame();
2531  for (un_iter iter = getSubUnits(); (su = *iter); ++iter)
2532  if (this->sim_atom_multiplier && su->sim_atom_multiplier) {
2533  //This ugly thing detects skipped frames.
2534  //This shouldn't happen during normal execution, as the interpolation will not be correct
2535  //when outside the expected range (that is, if the target queue slot is skipped).
2536  //BUT... this allows easy subunit simulation scattering by initializing cur_sim_frame
2537  //with random data.
2538  //Normal crossing
2539  if ( ( (su->last_processed_sqs < su->cur_sim_queue_slot) && (cur_sim_frame >= su->cur_sim_queue_slot) )
2540  //Full round trip
2541  || (su->last_processed_sqs == cur_sim_frame)
2542  //Incomplete round trip - but including target frame
2543  || ( (su->last_processed_sqs > cur_sim_frame)
2544  && ( (su->cur_sim_queue_slot <= cur_sim_frame) || (su->last_processed_sqs < su->cur_sim_queue_slot) ) )
2545  ) {
2546  if (do_subunit_scheduling) {
2547  int priority = UnitUtil::getPhysicsPriority( su );
2548  //Add some scattering
2549  priority = (priority+rand()%priority)/2;
2550  if (priority < 1) priority = 1;
2551  su->sim_atom_multiplier = this->sim_atom_multiplier*priority;
2554  if (su->sim_atom_multiplier < this->sim_atom_multiplier)
2555  su->sim_atom_multiplier = this->sim_atom_multiplier;
2556  } else {
2557  su->sim_atom_multiplier = this->sim_atom_multiplier;
2558  }
2559  SIMULATION_ATOM = basesimatom*(float) su->sim_atom_multiplier;
2561  cumulative_transformation,
2562  cumulative_transformation_matrix,
2563  cumulative_velocity,
2564  lastframe,
2565  uc,
2566  superunit );
2567  }
2568  }
2569  SIMULATION_ATOM = backup;
2570  }
2571 }
2572 
2574  const Transformation &trans,
2575  const Matrix &transmat,
2576  const Vector &CumulativeVelocity,
2577  bool lastframe,
2578  UnitCollection *uc,
2579  Unit *superunit )
2580 {
2581  subunit->UpdatePhysics( cumulative_transformation,
2582  cumulative_transformation_matrix,
2583  cumulative_velocity,
2584  lastframe,
2585  uc,
2586  superunit );
2587  //short fix
2588  subunit->cloaking = (unsigned int) cloaking;
2589  if (hull < 0) {
2590  subunit->Target( NULL );
2591  UnFire(); //don't want to go off shooting while your body's splitting everywhere
2592  }
2593 }
2594 
2595 float CalculateNearestWarpUnit( const Unit *thus, float minmultiplier, Unit **nearest_unit, bool count_negative_warp_units )
2596 {
2597  static float smallwarphack = XMLSupport::parse_float( vs_config->getVariable( "physics", "minwarpeffectsize", "100" ) );
2598  static float bigwarphack =
2599  XMLSupport::parse_float( vs_config->getVariable( "physics", "maxwarpeffectsize", "10000000" ) );
2600  //Boundary between multiplier regions 1&2. 2 is "high" mult
2601  static double warpregion1 = XMLSupport::parse_float( vs_config->getVariable( "physics", "warpregion1", "5000000" ) );
2602  //Boundary between multiplier regions 0&1 0 is mult=1
2603  static double warpregion0 = XMLSupport::parse_float( vs_config->getVariable( "physics", "warpregion0", "5000" ) );
2604  //Mult at 1-2 boundary
2605  static double warpcruisemult = XMLSupport::parse_float( vs_config->getVariable( "physics", "warpcruisemult", "5000" ) );
2606  //degree of curve
2607  static double curvedegree = XMLSupport::parse_float( vs_config->getVariable( "physics", "warpcurvedegree", "1.5" ) );
2608  //coefficient so as to agree with above
2609  static double upcurvek = warpcruisemult/pow( (warpregion1-warpregion0), curvedegree );
2610  //inverse fractional effect of ship vs real big object
2611  static float def_inv_interdiction = 1.
2612  /XMLSupport::parse_float( vs_config->getVariable( "physics", "default_interdiction",
2613  ".125" ) );
2614  Unit *planet;
2615  Unit *testthis = NULL;
2616  {
2617  NearestUnitLocator locatespec;
2619  testthis = locatespec.retval.unit;
2620  }
2622  (planet = *iter) || testthis;
2623  ++iter) {
2624  if ( !planet || !planet->Killed() ) {
2625  if (planet == NULL) {
2626  planet = testthis;
2627  testthis = NULL;
2628  }
2629  if (planet == thus)
2630  continue;
2631  float shiphack = 1;
2632  if (planet->isUnit() != PLANETPTR) {
2633  shiphack = def_inv_interdiction;
2634  if ( planet->specInterdiction != 0 && planet->graphicOptions.specInterdictionOnline != 0
2635  && (planet->specInterdiction > 0 || count_negative_warp_units) ) {
2636  shiphack = 1/fabs( planet->specInterdiction );
2637  if (thus->specInterdiction != 0 && thus->graphicOptions.specInterdictionOnline != 0)
2638  //only counters artificial interdiction ... or maybe it cheap ones shouldn't counter expensive ones!? or
2639  // expensive ones should counter planets...this is safe now, for gameplay
2640  shiphack *= fabs( thus->specInterdiction );
2641  }
2642  }
2643  float multipliertemp = 1;
2644  float minsizeeffect = (planet->rSize() > smallwarphack) ? planet->rSize() : smallwarphack;
2645  float effectiverad = minsizeeffect*( 1.0f+UniverseUtil::getPlanetRadiusPercent() )+thus->rSize();
2646  if (effectiverad > bigwarphack)
2647  effectiverad = bigwarphack;
2648  QVector dir = thus->Position()-planet->Position();
2649  double udist = dir.Magnitude();
2650  float sigdist = UnitUtil::getSignificantDistance( thus, planet );
2651  if ( planet->isPlanet() && udist < (1<<28) ) //If distance is viable as a float approximation and it's an actual celestial body
2652  udist = sigdist;
2653  do {
2654  double dist = udist;
2655  if (dist < 0) dist = 0;
2656  dist *= shiphack;
2657  if ( dist > (effectiverad+warpregion0) )
2658  multipliertemp = pow( (dist-effectiverad-warpregion0), curvedegree )*upcurvek;
2659  else
2660  multipliertemp = 1;
2661  if (multipliertemp < minmultiplier) {
2662  minmultiplier = multipliertemp;
2663  *nearest_unit = planet;
2664  //eventually use new multiplier to compute
2665  } else {break; }
2666  } while (0);
2667  if (!testthis)
2668  break; //don't want the ++
2669  }
2670  }
2671  return minmultiplier;
2672 }
2673 
2674 void Unit::AddVelocity( float difficulty )
2675 {
2676  Vector VelocityRef( 0, 0, 0 );
2677  {
2678  Unit *vr = computer.velocity_ref.GetUnit();
2679  if (vr)
2680  VelocityRef = vr->cumulative_velocity;
2681  }
2682  //for the heck of it.
2683  static float humanwarprampuptime = XMLSupport::parse_float( vs_config->getVariable( "physics", "warprampuptime", "5" ) );
2684  //for the heck of it.
2685  static float compwarprampuptime =
2686  XMLSupport::parse_float( vs_config->getVariable( "physics", "computerwarprampuptime", "10" ) );
2687  static float warprampdowntime = XMLSupport::parse_float( vs_config->getVariable( "physics", "warprampdowntime", "0.5" ) );
2688  Vector v = Velocity-VelocityRef;
2689  float len = v.Magnitude();
2690  float lastWarpField = graphicOptions.WarpFieldStrength;
2691  if (len > .01) //only get velocity going in DIRECTIOn of cumulative transformation for warp calc...
2692  v = v*( cumulative_transformation_matrix.getR().Dot( v*(1./len) ) );
2693  bool playa = _Universe->isPlayerStarship( this ) ? true : false;
2694  float warprampuptime = playa ? humanwarprampuptime : compwarprampuptime;
2695  //Warp Turning on/off
2696  if (graphicOptions.WarpRamping) {
2697  //Warp Turning on
2698  if (graphicOptions.InWarp == 1)
2699  graphicOptions.RampCounter = warprampuptime;
2700  //Warp Turning off
2701  else
2702  graphicOptions.RampCounter = warprampdowntime;
2703  graphicOptions.WarpRamping = 0;
2704  }
2705  if (graphicOptions.InWarp == 1 || graphicOptions.RampCounter != 0) {
2706  //Pi^2
2707  static float warpMultiplierMin =
2708  XMLSupport::parse_float( vs_config->getVariable( "physics", "warpMultiplierMin", "9.86960440109" ) );
2709  //C
2710  static float warpMultiplierMax =
2711  XMLSupport::parse_float( vs_config->getVariable( "physics", "warpMultiplierMax", "300000000" ) );
2712  //Pi^2 * C
2713  static float warpMaxEfVel = XMLSupport::parse_float( vs_config->getVariable( "physics", "warpMaxEfVel", "2960881320" ) );
2714  //inverse fractional effect of ship vs real big object
2715  float minmultiplier = warpMultiplierMax*graphicOptions.MaxWarpMultiplier;
2716  Unit *nearest_unit = NULL;
2717  minmultiplier = CalculateNearestWarpUnit( this, minmultiplier, &nearest_unit, true );
2718  float rampmult = 1;
2719  if (graphicOptions.RampCounter != 0) {
2720  graphicOptions.RampCounter -= SIMULATION_ATOM;
2721  if (graphicOptions.RampCounter <= 0)
2722  graphicOptions.RampCounter = 0;
2723  if (graphicOptions.InWarp == 0 && graphicOptions.RampCounter > warprampdowntime)
2724  graphicOptions.RampCounter = (1-graphicOptions.RampCounter/warprampuptime)*warprampdowntime;
2725  if (graphicOptions.InWarp == 1 && graphicOptions.RampCounter > warprampuptime)
2726  graphicOptions.RampCounter = warprampuptime;
2727  rampmult = (graphicOptions.InWarp) ? 1.0
2728  -( (graphicOptions.RampCounter
2729  /warprampuptime)
2730  *(graphicOptions.RampCounter
2731  /warprampuptime) ) : (graphicOptions.RampCounter
2732  /warprampdowntime)*(graphicOptions.RampCounter/warprampdowntime);
2733  }
2734  float minWarp = warpMultiplierMin*graphicOptions.MinWarpMultiplier;
2735  float maxWarp = warpMultiplierMax*graphicOptions.MaxWarpMultiplier;
2736  if (minmultiplier < minWarp)
2737  minmultiplier = minWarp;
2738  if (minmultiplier > maxWarp)
2739  minmultiplier = maxWarp; //SOFT LIMIT
2740  minmultiplier *= rampmult;
2741  if (minmultiplier < 1)
2742  minmultiplier = 1;
2743  v *= minmultiplier;
2744  float vmag = sqrt( v.i*v.i+v.j*v.j+v.k*v.k );
2745  if (vmag > warpMaxEfVel) {
2746  v *= warpMaxEfVel/vmag; //HARD LIMIT
2747  minmultiplier *= warpMaxEfVel/vmag;
2748  }
2749  graphicOptions.WarpFieldStrength = minmultiplier;
2750  } else {
2751  graphicOptions.WarpFieldStrength = 1;
2752  }
2753  //not any more? lastWarpField=1;
2754  if (graphicOptions.WarpFieldStrength != 1.0)
2755  v = GetWarpVelocity();
2756  else
2757  v = Velocity;
2758  static float WARPMEMORYEFFECT = XMLSupport::parse_float( vs_config->getVariable( "physics", "WarpMemoryEffect", "0.9" ) );
2759  graphicOptions.WarpFieldStrength = lastWarpField*WARPMEMORYEFFECT+(1.0-WARPMEMORYEFFECT)*graphicOptions.WarpFieldStrength;
2760  curr_physical_state.position = curr_physical_state.position+(v*SIMULATION_ATOM*difficulty).Cast();
2761  //now we do this later in update physics
2762  //I guess you have to, to be robust}
2763 }
2764 
2766  const Transformation &old_physical_state,
2767  const Vector &accel,
2768  float difficulty,
2769  const Matrix &transmat,
2770  const Vector &cum_vel,
2771  bool lastframe,
2772  UnitCollection *uc )
2773 {
2774  Cockpit *cp = _Universe->isPlayerStarship( this );
2775  //Only in non-networking OR networking && is a player OR SERVER && not a player
2776  if ( (Network == NULL && !SERVER) || (Network != NULL && cp && !SERVER) || (SERVER) )
2777  if (AngularVelocity.i || AngularVelocity.j || AngularVelocity.k)
2778  Rotate( SIMULATION_ATOM*(AngularVelocity) );
2779  //SERVERSIDE ONLY : If it is not a player, it is a unit controlled by server so compute changes
2780  if (SERVER) {
2781  AddVelocity( difficulty );
2782 
2783  cumulative_transformation = curr_physical_state;
2784  cumulative_transformation.Compose( trans, transmat );
2785  cumulative_transformation.to_matrix( cumulative_transformation_matrix );
2786  cumulative_velocity = TransformNormal( transmat, Velocity )+cum_vel;
2787  }
2788 }
2789 
2791 {
2792  if ( un->isSubUnit() )
2793  return un->Position();
2794  return un->LocalPosition();
2795 }
2796 
2797 static QVector AutoSafeEntrancePoint( const QVector start, float rsize, Unit *goal )
2798 {
2799  QVector def = UniverseUtil::SafeEntrancePoint( start, rsize );
2800  float bdis = ( def-RealPosition( goal ) ).MagnitudeSquared();
2801  for (int i = -1; i <= 1; ++i)
2802  for (int j = -1; j <= 1; ++j)
2803  for (int k = -1; k <= 1; k += 2) {
2804  QVector delta( i, j, k );
2805  delta.Normalize();
2806  QVector tmp = RealPosition( goal )+delta*(goal->rSize()+rsize);
2807  tmp = UniverseUtil::SafeEntrancePoint( tmp, rsize );
2808  float tmag = ( tmp-RealPosition( goal ) ).MagnitudeSquared();
2809  if (tmag < bdis) {
2810  bdis = tmag;
2811  def = tmp;
2812  }
2813  }
2814  return def;
2815 }
2816 
2817 float globQueryShell( QVector pos, QVector dir, float rad );
2818 std::string GenerateAutoError( Unit *me, Unit *targ )
2819 {
2820  if ( UnitUtil::isAsteroid( targ ) ) {
2821  static std::string err =
2822  XMLSupport::escaped_string( vs_config->getVariable( "graphics", "hud", "AsteroidsNearMessage",
2823  "#ff0000Asteroids Near#000000" ) );
2824  return err;
2825  }
2826  if ( targ->isPlanet() ) {
2827  static std::string err =
2828  XMLSupport::escaped_string( vs_config->getVariable( "graphics", "hud", "PlanetNearMessage",
2829  "#ff0000Planetary Hazard Near#000000" ) );
2830  return err;
2831  }
2832  if (targ->getRelation( me ) < 0) {
2833  static std::string err =
2834  XMLSupport::escaped_string( vs_config->getVariable( "graphics", "hud", "EnemyNearMessage",
2835  "#ff0000Enemy Near#000000" ) );
2836  return err;
2837  }
2838  static std::string err =
2839  XMLSupport::escaped_string( vs_config->getVariable( "graphics", "hud", "StarshipNearMessage",
2840  "#ff0000Starship Near#000000" ) );
2841  return err;
2842 }
2843 
2845  bool ignore_energy_requirements,
2846  std::string &failuremessage,
2847  int recursive_level )
2848 {
2849  static bool auto_valid =
2850  XMLSupport::parse_bool( vs_config->getVariable( "physics", "insystem_jump_or_timeless_auto-pilot", "false" ) );
2851  if (!auto_valid) {
2852  static std::string err = "No Insystem Jump";
2853  failuremessage = err;
2854  return false;
2855  }
2856  if (target->isUnit() == PLANETPTR) {
2857  Unit *targ = *(target->viewSubUnits());
2858  if (targ && 0 == targ->graphicOptions.FaceCamera)
2859  return AutoPilotToErrorMessage( targ, ignore_energy_requirements, failuremessage, recursive_level );
2860  }
2861  if (warpenergy < jump.insysenergy)
2862  if (!ignore_energy_requirements)
2863  return false;
2864  signed char Guaranteed = ComputeAutoGuarantee( this );
2865  if (Guaranteed == Mission::AUTO_OFF)
2866  return false;
2867  static float autopilot_term_distance =
2868  XMLSupport::parse_float( vs_config->getVariable( "physics", "auto_pilot_termination_distance", "6000" ) );
2869  static float atd_no_enemies =
2870  XMLSupport::parse_float( vs_config->getVariable( "physics", "auto_pilot_termination_distance_no_enemies",
2871  vs_config->getVariable( "physics", "auto_pilot_termination_distance",
2872  "6000" ) ) );
2873  static float autopilot_no_enemies_multiplier =
2874  XMLSupport::parse_float( vs_config->getVariable( "physics", "auto_pilot_no_enemies_distance_multiplier", "4" ) );
2875  if ( isSubUnit() ) {
2876  static std::string err = "Return To Cockpit for Auto";
2877  failuremessage = err;
2878  return false; //we can't auto here;
2879  }
2880  StarSystem *ss = activeStarSystem;
2881  if (ss == NULL)
2882  ss = _Universe->activeStarSystem();
2883  Unit *un = NULL;
2884  QVector start( Position() );
2885  QVector end( RealPosition( target ) );
2886  float totallength = (start-end).Magnitude();
2887  bool nanspace = false;
2888  if ( !FINITE( totallength ) ) {
2889  nanspace = true;
2890  start = QVector( 100000000.0, 100000000.0, 10000000000000.0 );
2891  totallength = (start-end).Magnitude();
2892  if ( !FINITE( totallength ) ) {
2893  end = QVector( 200000000.0, 100000000.0, 10000000000000.0 );
2894  totallength = (start-end).Magnitude();
2895  }
2896  }
2897  QVector endne( end );
2898 
2899  float totpercent = 1;
2900  if (totallength > 1) {
2901  float apt =
2902  (target->isUnit() == PLANETPTR) ? ( autopilot_term_distance+target->rSize()
2903  *UniverseUtil::getPlanetRadiusPercent() ) : autopilot_term_distance;
2904  float aptne =
2905  (target->isUnit() == PLANETPTR) ? ( atd_no_enemies+target->rSize()
2906  *UniverseUtil::getPlanetRadiusPercent() ) : atd_no_enemies;
2907  float percent = (getAutoRSize( this, this )+rSize()+target->rSize()+apt)/totallength;
2908  float percentne = (getAutoRSize( this, this )+rSize()+target->rSize()+aptne)/totallength;
2909  if (percentne > 1)
2910  endne = start;
2911 
2912  else
2913  endne = start*percentne+end*(1-percentne);
2914  if (percent > 1) {
2915  end = start;
2916  totpercent = 0;
2917  } else {
2918  totpercent *= (1-percent);
2919  end = start*percent+end*(1-percent);
2920  }
2921  }
2922  bool ok = true;
2923 
2924  static bool teleport_autopilot = XMLSupport::parse_bool( vs_config->getVariable( "physics", "teleport_autopilot", "true" ) );
2925  bool unsafe = false;
2926  if ( (!teleport_autopilot) && (!nanspace) ) {
2927  if (Guaranteed == Mission::AUTO_NORMAL && CloakVisible() > .5) {
2928  bool ignore_friendlies = true;
2929  for (un_iter i = ss->getUnitList().createIterator(); (un = *i) != NULL; ++i) {
2930  static bool canflythruplanets =
2931  XMLSupport::parse_bool( vs_config->getVariable( "physics", "can_auto_through_planets", "true" ) );
2932  if ( ( !(un->isUnit() == PLANETPTR
2933  && canflythruplanets) ) && un->isUnit() != NEBULAPTR && ( !UnitUtil::isSun( un ) ) ) {
2934  if (un != this && un != target) {
2935  float tdis = ( start-un->Position() ).Magnitude()-rSize()-un->rSize();
2936  float nedis = ( end-un->Position() ).Magnitude()-rSize()-un->rSize();
2937  float trad = getAutoRSize( this, un, ignore_friendlies )+getAutoRSize( this, this, ignore_friendlies );
2938  if (tdis <= trad) {
2939  failuremessage = GenerateAutoError( this, un );
2940  return false;
2941  }
2942  if ( (nedis < trad*autopilot_no_enemies_multiplier
2943  || tdis <= trad*autopilot_no_enemies_multiplier) && un->getRelation( this ) < 0 ) {
2944  unsafe = true;
2945  failuremessage = GenerateAutoError( this, un );
2946  }
2947  float intersection =
2948  globQueryShell( start-un->Position(), end-start, getAutoRSize( this,
2949  un,
2950  ignore_friendlies )+un->rSize() );
2951  if (intersection > 0) {
2952  unsafe = true;
2953  end = start+(end-start)*intersection;
2954  totpercent *= intersection;
2955  ok = false;
2956  failuremessage = GenerateAutoError( this, un );
2957  }
2958  }
2959  }
2960  }
2961  }
2962  } else if (!nanspace) {
2963  //just make sure we aren't in an asteroid field
2964  Unit *un;
2965  for (un_iter i = ss->getUnitList().createIterator(); (un = *i) != NULL; ++i)
2966  if ( UnitUtil::isAsteroid( un ) ) {
2967  static float minasteroiddistance =
2968  XMLSupport::parse_float( vs_config->getVariable( "physics", "min_asteroid_distance", "-100" ) );
2969  if (UnitUtil::getDistance( this, un ) < minasteroiddistance) {
2970  failuremessage = GenerateAutoError( this, un );
2971  return false; //no auto in roid field
2972  }
2973  }
2974  }
2975  bool nowhere = false;
2976  if (this != target) {
2977  if ( (end-start).MagnitudeSquared() < rSize()*rSize() ) {
2978  failuremessage =
2979  XMLSupport::escaped_string( vs_config->getVariable( "graphics", "hud", "AlreadyNearMessage",
2980  "#ff0000Already Near#000000" ) );
2981  return false;
2982  }
2983  warpenergy -= totpercent*jump.insysenergy;
2984  if (unsafe == false && totpercent == 0)
2985  end = endne;
2986  QVector sep( UniverseUtil::SafeEntrancePoint( end, rSize() ) );
2987  if ( (sep-end).MagnitudeSquared() > 16*rSize()*rSize() )
2988  //DOn't understand why rsize is so bigsep = AutoSafeEntrancePoint (end,(RealPosition(target)-end).Magnitude()-target->rSize(),target);
2989  sep = AutoSafeEntrancePoint( end, rSize(), target );
2990  if ( ( sep-RealPosition( target ) ).MagnitudeSquared()
2991  > ( RealPosition( this )-RealPosition( target ) ).MagnitudeSquared() ) {
2992  sep = RealPosition( this );
2993  nowhere = true;
2994  }
2995  static bool auto_turn_towards = XMLSupport::parse_bool( vs_config->getVariable( "physics", "auto_turn_towards", "true" ) );
2996  if (auto_turn_towards) {
2997  for (int i = 0; i < 3; ++i) {
2998  Vector methem( RealPosition( target ).Cast()-sep.Cast() );
2999  methem.Normalize();
3000  Vector p, q, r;
3001  GetOrientation( p, q, r );
3002  p = methem.Cross( r );
3003  float theta = p.Magnitude();
3004  if (theta*theta > .00001) {
3005  p *= (asin( theta )/theta);
3006  Rotate( p );
3007  GetOrientation( p, q, r );
3008  }
3009  if (r.Dot( methem ) < 0)
3010  Rotate( p*(PI/theta) );
3011  Velocity = methem*Velocity.Magnitude();
3012  }
3013  }
3014  static string insys_jump_ani = vs_config->getVariable( "graphics", "insys_jump_animation", "warp.ani" );
3015  if (
3016 
3017  insys_jump_ani.length() ) {
3018  static bool docache = true;
3019  if (docache) {
3020  UniverseUtil::cacheAnimation( insys_jump_ani );
3021  docache = false;
3022  }
3023  static float insys_jump_ani_size =
3024  XMLSupport::parse_float( vs_config->getVariable( "graphics", "insys_jump_animation_size", "4" ) );
3025  static float insys_jump_ani_growth =
3026  XMLSupport::parse_float( vs_config->getVariable( "graphics", "insys_jump_animation_growth", ".99" ) );
3027  UniverseUtil::playAnimationGrow( insys_jump_ani, RealPosition( this ),
3028  rSize()*insys_jump_ani_size, insys_jump_ani_growth );
3029 
3030  Vector v( GetVelocity() );
3031  v.Normalize();
3032  Vector p, q, r;
3033  GetOrientation( p, q, r );
3034  static float sec = XMLSupport::parse_float( vs_config->getVariable( "graphics", "insys_jump_ani_second_ahead", "4" ) );
3035  UniverseUtil::playAnimationGrow( insys_jump_ani, sep+GetVelocity()*sec+v*rSize(), rSize()*8, .97 );
3036  UniverseUtil::playAnimationGrow( insys_jump_ani, sep+GetVelocity()*sec+2*v*rSize()+r*4*rSize(), rSize()*16, .97 );
3037  }
3038  static bool warptrail = XMLSupport::parse_bool( vs_config->getVariable( "graphics", "warp_trail", "true" ) );
3039  if ( warptrail && (!nowhere) ) {
3040  static float warptrailtime = XMLSupport::parse_float( vs_config->getVariable( "graphics", "warp_trail_time", "20" ) );
3041  AddWarp( this, RealPosition( this ), warptrailtime );
3042  }
3043  if (!nowhere)
3044  SetCurPosition( sep );
3045  Cockpit *cp;
3046  if ( ( cp = _Universe->isPlayerStarship( this ) ) != NULL ) {
3047  std::string followermessage;
3048  if (getFlightgroup() != NULL) {
3049  Unit *other = NULL;
3050  if (recursive_level > 0) {
3051  for (un_iter ui = ss->getUnitList().createIterator(); NULL != (other = *ui); ++ui) {
3052  Flightgroup *ff = other->getFlightgroup();
3053  bool leadah = ( ff == getFlightgroup() );
3054  if (ff)
3055  if (ff->leader.GetUnit() == this)
3056  leadah = true;
3057  Order *otherord = other->getAIState();
3058  if (otherord) {
3059  if ( otherord->PursueTarget( this, leadah ) ) {
3060  other->AutoPilotToErrorMessage( this,
3061  ignore_energy_requirements,
3062  followermessage,
3063  recursive_level-1 );
3064  if (leadah)
3065  if ( NULL == _Universe->isPlayerStarship( other ) )
3066  other->SetPosition( AutoSafeEntrancePoint( LocalPosition(), other->rSize()*1.5, other ) );
3067  }
3068  }
3069  }
3070  }
3071  }
3072  }
3073  }
3074  return ok;
3075 }
3076 
3077 extern void ActivateAnimation( Unit *jp );
3079 {
3080  if (cp) {
3081  if (un->GetWarpEnergy() >= un->GetJumpStatus().energy)
3082  if (un->GetJumpStatus().drive > -2)
3083  cp->jumpok = 1;
3084  }
3085 }
3086 
3087 void Unit::DecreaseWarpEnergy( bool insys, float time )
3088 {
3089  static float bleedfactor = XMLSupport::parse_float( vs_config->getVariable( "physics", "warpbleed", "20" ) );
3090  static bool WCfuelhack = XMLSupport::parse_bool( vs_config->getVariable( "physics", "fuel_equals_warp", "false" ) );
3091  if (WCfuelhack)
3092  this->warpenergy = this->fuel;
3093  this->warpenergy -= (insys ? jump.insysenergy/bleedfactor : jump.energy)*time;
3094  if (this->warpenergy < 0)
3095  this->warpenergy = 0;
3096  if (WCfuelhack)
3097  this->fuel = this->warpenergy;
3098 }
3099 
3100 void Unit::IncreaseWarpEnergy( bool insys, float time )
3101 {
3102  static bool WCfuelhack = XMLSupport::parse_bool( vs_config->getVariable( "physics", "fuel_equals_warp", "false" ) );
3103  if (WCfuelhack)
3104  this->warpenergy = this->fuel;
3105  this->warpenergy += (insys ? jump.insysenergy : jump.energy)*time;
3106  if (this->warpenergy > this->maxwarpenergy)
3107  this->warpenergy = this->maxwarpenergy;
3108  if (WCfuelhack)
3109  this->fuel = this->warpenergy;
3110 }
3111 
3113 {
3114  static bool ai_jump_cheat = XMLSupport::parse_bool( vs_config->getVariable( "AI", "jump_without_energy", "false" ) );
3115  static bool nojumpinSPEC = XMLSupport::parse_bool( vs_config->getVariable( "physics", "noSPECJUMP", "true" ) );
3116  bool SPEC_interference = ( NULL != _Universe->isPlayerStarship( smalle ) ) ? smalle->graphicOptions.InWarp
3117  && nojumpinSPEC : ( NULL != _Universe->isPlayerStarship( this ) ) ? graphicOptions.InWarp
3118  && nojumpinSPEC : false;
3119  //only allow big with small
3120  if ( !GetDestinations().empty() ) {
3121  Cockpit *cp = _Universe->isPlayerStarship( smalle );
3122  if (!SPEC_interference || pImage->forcejump)
3123  TurnJumpOKLightOn( smalle, cp );
3124  else
3125  return false;
3126  //we have a drive
3127  if ( ( !SPEC_interference && ( smalle->GetJumpStatus().drive >= 0
3128  && //we have power
3129  (smalle->warpenergy >= smalle->GetJumpStatus().energy
3130  //or we're being cheap
3131  || (ai_jump_cheat && cp == NULL)
3132  ) ) )
3133  || pImage->forcejump ) {
3134  //or the jump is being forced?
3135  //NOW done in star_system_generic.cpp before TransferUnitToSystem smalle->warpenergy-=smalle->GetJumpStatus().energy;
3136  int dest = smalle->GetJumpStatus().drive;
3137  if (dest < 0)
3138  dest = 0;
3139  smalle->DeactivateJumpDrive();
3140  Unit *jumppoint = this;
3141  if (SERVER)
3142  VSServer->sendJump( smalle, this, GetDestinations()[dest%GetDestinations().size()] );
3143  else
3144  _Universe->activeStarSystem()->JumpTo( smalle, jumppoint, GetDestinations()[dest%GetDestinations().size()] );
3145  return true;
3146  }
3147  return true;
3148  }
3149  if ( !smalle->GetDestinations().empty() ) {
3150  Cockpit *cp = _Universe->isPlayerStarship( this );
3151  if (!SPEC_interference || smalle->pImage->forcejump)
3152  TurnJumpOKLightOn( this, cp );
3153  else
3154  return false;
3155  if ( ( !SPEC_interference && (GetJumpStatus().drive >= 0
3156  && ( warpenergy >= GetJumpStatus().energy || (ai_jump_cheat && cp == NULL) )
3157  ) ) || smalle->pImage->forcejump ) {
3158  warpenergy -= GetJumpStatus().energy;
3159  DeactivateJumpDrive();
3160  Unit *jumppoint = smalle;
3161  if (SERVER) {
3162  VSServer->sendJump( this, smalle,
3163  smalle->GetDestinations()[GetJumpStatus().drive%smalle->GetDestinations().size()] );
3164  } else {
3165  _Universe->activeStarSystem()->JumpTo( this, jumppoint,
3166  smalle->GetDestinations()[GetJumpStatus().drive
3167  %smalle->GetDestinations().size()] );
3168  }
3169  return true;
3170  }
3171  return true;
3172  }
3173  return false;
3174 }
3175 
3176 Cockpit* Unit::GetVelocityDifficultyMult( float &difficulty ) const
3177 {
3178  difficulty = 1;
3179  Cockpit *player_cockpit = _Universe->isPlayerStarship( this );
3180  if ( (player_cockpit) == NULL ) {
3181  static float exp = XMLSupport::parse_float( vs_config->getVariable( "physics", "difficulty_speed_exponent", ".2" ) );
3182  difficulty = pow( g_game.difficulty, exp );
3183  }
3184  return player_cockpit;
3185 }
3186 
3187 void Unit::Rotate( const Vector &axis )
3188 {
3189  double theta = axis.Magnitude();
3190  double ootheta = 0;
3191  if (theta == 0) return;
3192  ootheta = 1/theta;
3193  float s = cos( theta*.5 );
3194  Quaternion rot = Quaternion( s, axis*(sinf( theta*.5 )*ootheta) );
3195  if (theta < 0.0001)
3196  rot = identity_quaternion;
3197  curr_physical_state.orientation *= rot;
3198  if (limits.limitmin > -1) {
3199  Matrix mat;
3200  curr_physical_state.orientation.to_matrix( mat );
3201  if (limits.structurelimits.Dot( mat.getR() ) < limits.limitmin)
3202  curr_physical_state.orientation = prev_physical_state.orientation;
3203  }
3204 }
3205 
3206 void Unit::FireEngines( const Vector &Direction /*unit vector... might default to "r"*/, float FuelSpeed, float FMass )
3207 {
3208  static float fuelpct = XMLSupport::parse_float( vs_config->getVariable( "physics", "FuelUsage", "1" ) );
3209  fuel -= fuelpct*FMass;
3210  if (fuel < 0) {
3211  FMass += fuel;
3212  fuel = 0; //ha ha!
3213  }
3214  NetForce += Direction*( FuelSpeed*FMass/GetElapsedTime() );
3215 }
3216 
3217 //applies a force for the whole gameturn upon the center of mass
3219 {
3220  if ( FINITE( Vforce.i ) && FINITE( Vforce.j ) && FINITE( Vforce.k ) )
3221  NetForce += Vforce;
3222  else
3223  VSFileSystem::vs_fprintf( stderr, "fatal force" );
3224 }
3225 
3226 //applies a force for the whole gameturn upon the center of mass
3228 {
3229  if ( FINITE( Vforce.i ) && FINITE( Vforce.j ) && FINITE( Vforce.k ) )
3230  NetLocalForce += Vforce;
3231  else
3232  VSFileSystem::vs_fprintf( stderr, "fatal local force" );
3233 }
3234 
3236 {
3237  if ( FINITE( Vforce.i ) && FINITE( Vforce.j ) && FINITE( Vforce.k ) )
3238  NetForce += Vforce*GetMass();
3239  else
3240  VSFileSystem::vs_fprintf( stderr, "fatal force" );
3241 }
3242 
3243 void Unit::ApplyTorque( const Vector &Vforce, const QVector &Location )
3244 {
3245  //Not completely correct
3246  NetForce += Vforce;
3247  NetTorque += Vforce.Cross( (Location-curr_physical_state.position).Cast() );
3248 }
3249 
3250 void Unit::ApplyLocalTorque( const Vector &Vforce, const Vector &Location )
3251 {
3252  NetForce += Vforce;
3253  NetTorque += Vforce.Cross( Location );
3254 }
3255 
3256 //usually from thrusters remember if I have 2 balanced thrusters I should multiply their effect by 2 :)
3257 void Unit::ApplyBalancedLocalTorque( const Vector &Vforce, const Vector &Location )
3258 {
3259  NetTorque += Vforce.Cross( Location );
3260 }
3261 
3262 void Unit::ApplyLocalTorque( const Vector &torque )
3263 {
3264  NetLocalTorque += ClampTorque( torque );
3265 }
3266 
3268 {
3269  //torque is a normal
3270  return torque*(Vector( copysign( limits.pitch, torque.i ),
3271  copysign( limits.yaw, torque.j ),
3272  copysign( limits.roll, torque.k ) )*torque);
3273 }
3274 
3275 float GetFuelUsage( bool afterburner )
3276 {
3277  static float normalfuelusage = XMLSupport::parse_float( vs_config->getVariable( "physics", "FuelUsage", "1" ) );
3278  static float abfuelusage = XMLSupport::parse_float( vs_config->getVariable( "physics", "AfterburnerFuelUsage", "4" ) );
3279  if (afterburner)
3280  return abfuelusage;
3281  return normalfuelusage;
3282 }
3283 
3285 {
3286  Vector Res = amt1;
3287  static bool WCfuelhack = XMLSupport::parse_bool( vs_config->getVariable( "physics", "fuel_equals_warp", "false" ) );
3288  if (WCfuelhack)
3289  fuel = warpenergy;
3290  static float staticfuelclamp = XMLSupport::parse_float( vs_config->getVariable( "physics", "NoFuelThrust", ".4" ) );
3291  float fuelclamp = (fuel <= 0) ? staticfuelclamp : 1;
3292  if (fabs( amt1.i ) > fuelclamp*limits.pitch)
3293  Res.i = copysign( fuelclamp*limits.pitch, amt1.i );
3294  if (fabs( amt1.j ) > fuelclamp*limits.yaw)
3295  Res.j = copysign( fuelclamp*limits.yaw, amt1.j );
3296  if (fabs( amt1.k ) > fuelclamp*limits.roll)
3297  Res.k = copysign( fuelclamp*limits.roll, amt1.k );
3298  static float Lithium6constant =
3299  XMLSupport::parse_float( vs_config->getVariable( "physics", "LithiumRelativeEfficiency_Lithium", "1" ) );
3300  //1/5,000,000 m/s
3301  static float FMEC_exit_vel_inverse =
3302  XMLSupport::parse_float( vs_config->getVariable( "physics", "FMEC_exit_vel", "0.0000002" ) );
3303  //HACK this forces the reaction to be Li-6+D fusion with efficiency governed by the getFuelUsage function
3304  fuel -= GetFuelUsage( false )*SIMULATION_ATOM*Res.Magnitude()*FMEC_exit_vel_inverse/Lithium6constant;
3305 #ifndef __APPLE__
3306  if ( ISNAN( fuel ) ) {
3307  fprintf( stderr, "FUEL is NAN\n" );
3308  fuel = 0;
3309  }
3310 #endif
3311  if (fuel < 0) fuel = 0;
3312  if (warpenergy < 0) warpenergy = 0;
3313  if (WCfuelhack) warpenergy = fuel;
3314  return Res;
3315 }
3316 
3318 {
3319  static float combat_mode_mult = XMLSupport::parse_float( vs_config->getVariable( "physics", "combat_speed_boost", "100" ) );
3320  return (!combat_mode) ? combat_mode_mult*max_combat_speed : max_combat_speed;
3321 }
3322 
3324 {
3325  static float combat_mode_mult = XMLSupport::parse_float( vs_config->getVariable( "physics", "combat_speed_boost", "100" ) );
3326  //same capped big speed as combat...else different
3327  return (!combat_mode) ? combat_mode_mult*max_combat_speed : max_combat_ab_speed;
3328 }
3329 
3331 {
3332  if (computer.combat_mode)
3333  computer.combat_mode = false;
3334  else
3335  computer.combat_mode = true;
3336 }
3337 
3339 {
3340  return computer.combat_mode;
3341 }
3342 
3343 Vector Unit::ClampVelocity( const Vector &velocity, const bool afterburn )
3344 {
3345  static float staticfuelclamp = XMLSupport::parse_float( vs_config->getVariable( "physics", "NoFuelThrust", ".4" ) );
3346  static float staticabfuelclamp = XMLSupport::parse_float( vs_config->getVariable( "physics", "NoFuelAfterburn", ".1" ) );
3347  float fuelclamp = (fuel <= 0) ? staticfuelclamp : 1;
3348  float abfuelclamp = ( fuel <= 0 || (energy < afterburnenergy*SIMULATION_ATOM) ) ? staticabfuelclamp : 1;
3349  float limit =
3350  afterburn ? ( abfuelclamp
3351  *( computer.max_ab_speed()
3352  -computer.max_speed() )+( fuelclamp*computer.max_speed() ) ) : fuelclamp*computer.max_speed();
3353  float tmp = velocity.Magnitude();
3354  if ( tmp > fabs( limit ) )
3355  return velocity*(limit/tmp);
3356  return velocity;
3357 }
3358 
3360 {
3361  for (unsigned int j = 0; j < mounts.size(); ++j) {
3362  DestroyMount( &mounts[j] );
3363  AUDDeleteSound( mounts[j].sound );
3364  if (mounts[j].ref.gun && mounts[j].type->type == weapon_info::BEAM) {
3365  //hope we're not killin' em twice...they don't go in gunqueue
3366  delete mounts[j].ref.gun;
3367  mounts[j].ref.gun = NULL;
3368  }
3369  }
3370  mounts.clear();
3371  Unit *su;
3372  for (un_iter i = getSubUnits(); (su = *i) != NULL; ++i)
3373  su->ClearMounts();
3374 }
3375 
3376 Vector Unit::ClampAngVel( const Vector &velocity )
3377 {
3378  Vector res( velocity );
3379  if (res.i >= 0) {
3380  if (res.i > computer.max_pitch_down)
3381  res.i = computer.max_pitch_down;
3382  } else if (-res.i > computer.max_pitch_up) {
3383  res.i = -computer.max_pitch_up;
3384  }
3385  if (res.j >= 0) {
3386  if (res.j > computer.max_yaw_left)
3387  res.j = computer.max_yaw_left;
3388  } else if (-res.j > computer.max_yaw_right) {
3389  res.j = -computer.max_yaw_right;
3390  }
3391  if (res.k >= 0) {
3392  if (res.k > computer.max_roll_left)
3393  res.k = computer.max_roll_left;
3394  } else if (-res.k > computer.max_roll_right) {
3395  res.k = -computer.max_roll_right;
3396  }
3397  return res;
3398 }
3399 
3401 {
3402  //amt1 is a normal
3403  return amt1*(Vector( copysign( limits.lateral, amt1.i ),
3404  copysign( limits.vertical, amt1.j ),
3405  amt1.k > 0 ? limits.forward : -limits.retro )*amt1);
3406 }
3407 
3408 //CMD_FLYBYWIRE depends on new version of Clampthrust... don't change without resolving it
3409 
3410 Vector Unit::ClampThrust( const Vector &amt1, bool afterburn )
3411 {
3412  static bool WCfuelhack = XMLSupport::parse_bool( vs_config->getVariable( "physics", "fuel_equals_warp", "false" ) );
3413  static float staticfuelclamp = XMLSupport::parse_float( vs_config->getVariable( "physics", "NoFuelThrust", ".4" ) );
3414  static float staticabfuelclamp = XMLSupport::parse_float( vs_config->getVariable( "physics", "NoFuelAfterburn", ".1" ) );
3415  static bool finegrainedFuelEfficiency =
3416  XMLSupport::parse_bool( vs_config->getVariable( "physics", "VariableFuelConsumption", "false" ) );
3417  if (WCfuelhack) {
3418  if (fuel > warpenergy)
3419  fuel = warpenergy;
3420  if (fuel < warpenergy)
3421  warpenergy = fuel;
3422  }
3423  float instantenergy = afterburnenergy*SIMULATION_ATOM;
3424  if ( (afterburntype == 0) && energy < instantenergy )
3425  afterburn = false;
3426  if ( (afterburntype == 1) && fuel < 0 ) {
3427  fuel = 0;
3428  afterburn = false;
3429  }
3430  if ( (afterburntype == 2) && warpenergy < 0 ) {
3431  warpenergy = 0;
3432  afterburn = false;
3433  }
3434  if (3 == afterburntype) //no afterburner -- we should really make these types an enum :-/
3435  afterburn = false;
3436  Vector Res = amt1;
3437 
3438  float fuelclamp = (fuel <= 0) ? staticfuelclamp : 1;
3439  float abfuelclamp = (fuel <= 0) ? staticabfuelclamp : 1;
3440  if ( fabs( amt1.i ) > fabs( fuelclamp*limits.lateral ) )
3441  Res.i = copysign( fuelclamp*limits.lateral, amt1.i );
3442  if ( fabs( amt1.j ) > fabs( fuelclamp*limits.vertical ) )
3443  Res.j = copysign( fuelclamp*limits.vertical, amt1.j );
3444  float ablimit =
3445  afterburn
3446  ? ( (limits.afterburn-limits.forward)*abfuelclamp+limits.forward*fuelclamp )
3447  : limits.forward;
3448  if (amt1.k > ablimit)
3449  Res.k = ablimit;
3450  if (amt1.k < -limits.retro)
3451  Res.k = -limits.retro;
3452  static float Lithium6constant =
3453  XMLSupport::parse_float( vs_config->getVariable( "physics", "DeuteriumRelativeEfficiency_Lithium", "1" ) );
3454  //1/5,000,000 m/s
3455  static float FMEC_exit_vel_inverse =
3456  XMLSupport::parse_float( vs_config->getVariable( "physics", "FMEC_exit_vel", "0.0000002" ) );
3457  if (afterburntype == 2) {
3458  //Energy-consuming afterburner
3459  //HACK this forces the reaction to be Li-6+Li-6 fusion with efficiency governed by the getFuelUsage function
3460  warpenergy -= afterburnenergy*GetFuelUsage( afterburn )*SIMULATION_ATOM*Res.Magnitude()*FMEC_exit_vel_inverse
3461  /Lithium6constant;
3462  }
3463  if (3 == afterburntype || afterburntype == 1) {
3464  //fuel-burning overdrive - uses afterburner efficiency. In NO_AFTERBURNER case, "afterburn" will always be false, so can reuse code.
3465  //HACK this forces the reaction to be Li-6+Li-6 fusion with efficiency governed by the getFuelUsage function
3466  fuel -=
3467  ( (afterburn
3468  && finegrainedFuelEfficiency) ? afterburnenergy : GetFuelUsage( afterburn ) )*SIMULATION_ATOM*Res.Magnitude()
3469  *FMEC_exit_vel_inverse/Lithium6constant;
3470 #ifndef __APPLE__
3471  if ( ISNAN( fuel ) ) {
3472  fprintf( stderr, "Fuel is NAN A\n" );
3473  fuel = 0;
3474  }
3475 #endif
3476  }
3477  if (afterburntype == 0) {
3478  //fuel-burning afterburner - uses default efficiency - appears to check for available energy? FIXME
3479  //HACK this forces the reaction to be Li-6+Li-6 fusion with efficiency governed by the getFuelUsage function
3480  fuel -= GetFuelUsage( false )*SIMULATION_ATOM*Res.Magnitude()*FMEC_exit_vel_inverse/Lithium6constant;
3481 #ifndef __APPLE__
3482  if ( ISNAN( fuel ) ) {
3483  fprintf( stderr, "Fuel is NAN B\n" );
3484  fuel = 0;
3485  }
3486 #endif
3487  }
3488  if ( (afterburn) && (afterburntype == 0) )
3489  energy -= instantenergy;
3490  if (WCfuelhack) {
3491  if (fuel > warpenergy)
3492  fuel = warpenergy;
3493  if (fuel < warpenergy)
3494  warpenergy = fuel;
3495  }
3496  return Res;
3497 }
3498 
3499 void Unit::Thrust( const Vector &amt1, bool afterburn )
3500 {
3501  Vector amt = ClampThrust( amt1, afterburn );
3502  ApplyLocalForce( amt );
3503 }
3504 
3505 void Unit::LateralThrust( float amt )
3506 {
3507  if (amt > 1.0) amt = 1.0;
3508  if (amt < -1.0) amt = -1.0;
3509  ApplyLocalForce( amt*limits.lateral*Vector( 1, 0, 0 ) );
3510 }
3511 
3512 void Unit::VerticalThrust( float amt )
3513 {
3514  if (amt > 1.0) amt = 1.0;
3515  if (amt < -1.0) amt = -1.0;
3516  ApplyLocalForce( amt*limits.vertical*Vector( 0, 1, 0 ) );
3517 }
3518 
3519 void Unit::LongitudinalThrust( float amt )
3520 {
3521  if (amt > 1.0) amt = 1.0;
3522  if (amt < -1.0) amt = -1.0;
3523  ApplyLocalForce( amt*limits.forward*Vector( 0, 0, 1 ) );
3524 }
3525 
3526 void Unit::YawTorque( float amt )
3527 {
3528  if (amt > limits.yaw) amt = limits.yaw;
3529  else if (amt < -limits.yaw)
3530  amt = -limits.yaw;
3531  ApplyLocalTorque( amt*Vector( 0, 1, 0 ) );
3532 }
3533 
3534 void Unit::PitchTorque( float amt )
3535 {
3536  if (amt > limits.pitch) amt = limits.pitch;
3537  else if (amt < -limits.pitch)
3538  amt = -limits.pitch;
3539  ApplyLocalTorque( amt*Vector( 1, 0, 0 ) );
3540 }
3541 
3542 void Unit::RollTorque( float amt )
3543 {
3544  if (amt > limits.roll) amt = limits.roll;
3545  else if (amt < -limits.roll)
3546  amt = -limits.roll;
3547  ApplyLocalTorque( amt*Vector( 0, 0, 1 ) );
3548 }
3549 
3551 {
3552  static float warpenergymultiplier =
3553  XMLSupport::parse_float( vs_config->getVariable( "physics", "warp_energy_multiplier", "0.12" ) );
3554  static float playerwarpenergymultiplier =
3555  XMLSupport::parse_float( vs_config->getVariable( "physics", "warp_energy_player_multiplier", ".12" ) );
3556  bool player = _Universe->isPlayerStarship( un ) != NULL;
3557  Flightgroup *fg = un->getFlightgroup();
3558  if (fg && !player)
3559  player = _Universe->isPlayerStarship( fg->leader.GetUnit() ) != NULL;
3560  return player ? playerwarpenergymultiplier : warpenergymultiplier;
3561 }
3562 
3563 //short fix
3564 static bool applyto( float &shield, const float max, const float amt )
3565 {
3566  shield += amt; //short fix
3567  if (shield > max)
3568  shield = max;
3569  return (shield >= max) ? 1 : 0;
3570 }
3571 
3572 float totalShieldVal( const Shield &shield )
3573 {
3574  float maxshield = 0;
3575  switch (shield.number)
3576  {
3577  case 2:
3578  maxshield = shield.shield2fb.frontmax+shield.shield2fb.backmax;
3579  break;
3580  case 4:
3581  maxshield = shield.shield4fbrl.frontmax+shield.shield4fbrl.backmax+shield.shield4fbrl.leftmax
3582  +shield.shield4fbrl.rightmax;
3583  break;
3584  case 8:
3585  maxshield = shield.shield8.frontrighttopmax+shield.shield8.backrighttopmax+shield.shield8.frontlefttopmax
3586  +shield.shield8.backlefttopmax+shield.shield8.frontrightbottommax+shield.shield8.backrightbottommax
3587  +shield.shield8.frontleftbottommax+shield.shield8.backleftbottommax;
3588  break;
3589  }
3590  return maxshield;
3591 }
3592 
3593 float currentTotalShieldVal( const Shield &shield )
3594 {
3595  float maxshield = 0;
3596  switch (shield.number)
3597  {
3598  case 2:
3599  maxshield = shield.shield2fb.front+shield.shield2fb.back;
3600  break;
3601  case 4:
3602  maxshield = shield.shield4fbrl.front+shield.shield4fbrl.back+shield.shield4fbrl.left+shield.shield4fbrl.right;
3603  break;
3604  case 8:
3605  maxshield = shield.shield8.frontrighttop+shield.shield8.backrighttop+shield.shield8.frontlefttop
3606  +shield.shield8.backlefttop+shield.shield8.frontrightbottom+shield.shield8.backrightbottom
3607  +shield.shield8.frontleftbottom+shield.shield8.backleftbottom;
3608  break;
3609  }
3610  return maxshield;
3611 }
3612 
3613 float totalShieldEnergyCapacitance( const Shield &shield )
3614 {
3615  static float shieldenergycap =
3616  XMLSupport::parse_float( vs_config->getVariable( "physics", "shield_energy_capacitance", ".2" ) );
3617  static bool use_max_shield_value =
3618  XMLSupport::parse_bool( vs_config->getVariable( "physics", "use_max_shield_energy_usage", "false" ) );
3619  return shieldenergycap*use_max_shield_value ? totalShieldVal( shield ) : currentTotalShieldVal( shield );
3620 }
3621 
3622 float Unit::MaxShieldVal() const
3623 {
3624  float maxshield = 0;
3625  switch (shield.number)
3626  {
3627  case 2:
3628  maxshield = .5*(shield.shield2fb.frontmax+shield.shield2fb.backmax);
3629  break;
3630  case 4:
3631  maxshield = .25
3632  *(shield.shield4fbrl.frontmax+shield.shield4fbrl.backmax+shield.shield4fbrl.leftmax
3633  +shield.shield4fbrl.rightmax);
3634  break;
3635  case 8:
3636  maxshield = .125
3637  *(shield.shield8.frontrighttopmax+shield.shield8.backrighttopmax+shield.shield8.frontlefttopmax
3638  +shield.shield8.backlefttopmax+shield.shield8.frontrightbottommax+shield.shield8.backrightbottommax
3639  +shield.shield8.frontleftbottommax+shield.shield8.backleftbottommax);
3640  break;
3641  }
3642  return maxshield;
3643 }
3644 
3645 void Unit::RechargeEnergy()
3646 {
3647  static bool reactor_uses_fuel = XMLSupport::parse_bool( vs_config->getVariable( "physics", "reactor_uses_fuel", "false" ) );
3648  if ( (!reactor_uses_fuel) || (fuel > 0) )
3649  energy += recharge*SIMULATION_ATOM;
3650 }
3651 
3653 {
3654  static bool shields_in_spec = XMLSupport::parse_bool( vs_config->getVariable( "physics", "shields_in_spec", "false" ) );
3655  static float shieldenergycap =
3656  XMLSupport::parse_float( vs_config->getVariable( "physics", "shield_energy_capacitance", ".2" ) );
3657  static bool energy_before_shield =
3658  XMLSupport::parse_bool( vs_config->getVariable( "physics", "engine_energy_priority", "true" ) );
3659  static bool apply_difficulty_shields =
3660  XMLSupport::parse_bool( vs_config->getVariable( "physics", "difficulty_based_shield_recharge", "true" ) );
3661  static float shield_maintenance_cost =
3662  XMLSupport::parse_float( vs_config->getVariable( "physics", "shield_maintenance_charge", ".25" ) );
3663  static bool shields_require_power =
3664  XMLSupport::parse_bool( vs_config->getVariable( "physics", "shields_require_passive_recharge_maintenance", "true" ) );
3665  static float discharge_per_second =
3666  XMLSupport::parse_float( vs_config->getVariable( "physics", "speeding_discharge", ".25" ) );
3667  //approx
3668  const float dischargerate = (1-(1-discharge_per_second)*SIMULATION_ATOM);
3669  static float min_shield_discharge =
3670  XMLSupport::parse_float( vs_config->getVariable( "physics", "min_shield_speeding_discharge", ".1" ) );
3671  static float low_power_mode =
3672  XMLSupport::parse_float( vs_config->getVariable( "physics", "low_power_mode_energy", "10" ) );
3673  static float max_shield_lowers_recharge =
3674  XMLSupport::parse_float( vs_config->getVariable( "physics", "max_shield_recharge_drain", "0" ) );
3675  static bool max_shield_lowers_capacitance =
3676  XMLSupport::parse_bool( vs_config->getVariable( "physics", "max_shield_lowers_capacitance", "false" ) );
3677  static bool reactor_uses_fuel =
3678  XMLSupport::parse_bool( vs_config->getVariable( "physics", "reactor_uses_fuel", "false" ) );
3679  static float reactor_idle_efficiency =
3680  XMLSupport::parse_float( vs_config->getVariable( "physics", "reactor_idle_efficiency", "0.98" ) );
3681  static float VSD = XMLSupport::parse_float( vs_config->getVariable( "physics", "VSD_MJ_yield", "5.4" ) );
3682  //Fuel Mass in metric tons expended per generation of 100MJ
3683  static float FMEC_factor = XMLSupport::parse_float( vs_config->getVariable( "physics", "FMEC_factor", "0.000000008" ) );
3684  int rechargesh = 1; //used ... oddly
3685  float maxshield = totalShieldEnergyCapacitance( shield );
3686  bool velocity_discharge = false;
3687  float rec = 0;
3688  float precharge = energy;
3689  //Reactor energy
3690  if (!energy_before_shield)
3691  RechargeEnergy();
3692  //Shield energy drain
3693  if (shield.number) {
3694  //GAHHH reactor in units of 100MJ, shields in units of VSD=5.4MJ to make 1MJ of shield use 1/shieldenergycap MJ
3695  if (shields_in_spec || !graphicOptions.InWarp) {
3696  energy -= shield.recharge*VSD
3697  /( 100
3698  *(shield.efficiency ? shield.efficiency : 1) )/shieldenergycap*shield.number*shield_maintenance_cost
3699  *SIMULATION_ATOM*( (apply_difficulty_shields) ? g_game.difficulty : 1 );
3700  if (energy < 0) {
3701  velocity_discharge = true;
3702  energy = 0;
3703  }
3704  }
3705  rec =
3706  (velocity_discharge) ? 0 : ( (shield.recharge*VSD/100*SIMULATION_ATOM*shield.number/shieldenergycap)
3707  > energy ) ? (energy*shieldenergycap*100/VSD
3708  /shield.number) : shield.recharge*SIMULATION_ATOM;
3709  if (apply_difficulty_shields) {
3710  if ( !_Universe->isPlayerStarship( this ) )
3711  rec *= g_game.difficulty;
3712  else
3713  rec *= g_game.difficulty;
3714  }
3715  if (graphicOptions.InWarp && !shields_in_spec) {
3716  rec = 0;
3717  velocity_discharge = true;
3718  }
3719  if (GetNebula() != NULL) {
3720  static float nebshields =
3721  XMLSupport::parse_float( vs_config->getVariable( "physics", "nebula_shield_recharge", ".5" ) );
3722  rec *= nebshields;
3723  }
3724  }
3725  //ECM energy drain
3726  if (computer.ecmactive) {
3727  static float ecmadj = XMLSupport::parse_float( vs_config->getVariable( "physics", "ecm_energy_cost", ".05" ) );
3728  float sim_atom_ecm = ecmadj*pImage->ecm*SIMULATION_ATOM;
3729  if (energy > sim_atom_ecm)
3730  energy -= sim_atom_ecm;
3731  else
3732  energy = 0;
3733  }
3734  //Shield regeneration
3735  switch (shield.number)
3736  {
3737  case 2:
3738  shield.shield2fb.front += rec;
3739  shield.shield2fb.back += rec;
3740  if (shield.shield2fb.front > shield.shield2fb.frontmax)
3741  shield.shield2fb.front = shield.shield2fb.frontmax;
3742  else
3743  rechargesh = 0;
3744  if (shield.shield2fb.back > shield.shield2fb.backmax)
3745  shield.shield2fb.back = shield.shield2fb.backmax;
3746 
3747  else
3748  rechargesh = 0;
3749  if (velocity_discharge) {
3750  if (shield.shield2fb.back > min_shield_discharge*shield.shield2fb.backmax)
3751  shield.shield2fb.back *= dischargerate;
3752  if (shield.shield2fb.front > min_shield_discharge*shield.shield2fb.frontmax)
3753  shield.shield2fb.front *= dischargerate;
3754  }
3755  rec = rec*2/shieldenergycap*VSD/100;
3756  break;
3757  case 4:
3758  rechargesh =
3759  applyto( shield.shield4fbrl.front, shield.shield4fbrl.frontmax,
3760  rec )*( applyto( shield.shield4fbrl.back, shield.shield4fbrl.backmax, rec ) )*applyto(
3761  shield.shield4fbrl.right,
3762  shield.shield4fbrl.
3763  rightmax,
3764  rec )*applyto(
3765  shield.shield4fbrl.left,
3766  shield.shield4fbrl.leftmax,
3767  rec );
3768  if (velocity_discharge) {
3769  if (shield.shield4fbrl.front > min_shield_discharge*shield.shield4fbrl.frontmax)
3770  shield.shield4fbrl.front *= dischargerate;
3771  if (shield.shield4fbrl.left > min_shield_discharge*shield.shield4fbrl.leftmax)
3772  shield.shield4fbrl.left *= dischargerate;
3773  if (shield.shield4fbrl.back > min_shield_discharge*shield.shield4fbrl.backmax)
3774  shield.shield4fbrl.back *= dischargerate;
3775  if (shield.shield4fbrl.right > min_shield_discharge*shield.shield4fbrl.rightmax)
3776  shield.shield4fbrl.right *= dischargerate;
3777  }
3778  rec = rec*4/shieldenergycap*VSD/100;
3779  break;
3780  case 8:
3781  rechargesh =
3782  applyto( shield.shield8.frontrighttop, shield.shield8.frontrighttopmax,
3783  rec )*( applyto( shield.shield8.backrighttop, shield.shield8.backrighttopmax, rec ) )*applyto(
3784  shield.shield8.frontlefttop,
3785  shield.shield8.frontlefttopmax,
3786  rec )*applyto( shield.shield8.backlefttop, shield.shield8.backlefttopmax, rec )*applyto(
3787  shield.shield8.frontrightbottom,
3788  shield.shield8.
3789  frontrightbottommax,
3790  rec )
3791  *( applyto( shield.shield8.backrightbottom, shield.shield8.backrightbottommax, rec ) )*applyto(
3792  shield.shield8.frontleftbottom,
3793  shield.shield8.frontleftbottommax,
3794  rec )*applyto( shield.shield8.backleftbottom, shield.shield8.backleftbottommax, rec );
3795  if (velocity_discharge) {
3796  if (shield.shield8.frontrighttop > min_shield_discharge*shield.shield8.frontrighttopmax)
3797  shield.shield8.frontrighttop *= dischargerate;
3798  if (shield.shield8.frontlefttop > min_shield_discharge*shield.shield8.frontlefttopmax)
3799  shield.shield8.frontlefttop *= dischargerate;
3800  if (shield.shield8.backrighttop > min_shield_discharge*shield.shield8.backrighttopmax)
3801  shield.shield8.backrighttop *= dischargerate;
3802  if (shield.shield8.backlefttop > min_shield_discharge*shield.shield8.backlefttopmax)
3803  shield.shield8.backlefttop *= dischargerate;
3804  if (shield.shield8.frontrightbottom > min_shield_discharge*shield.shield8.frontrightbottommax)
3805  shield.shield8.frontrightbottom *= dischargerate;
3806  if (shield.shield8.frontleftbottom > min_shield_discharge*shield.shield8.frontleftbottommax)
3807  shield.shield8.frontleftbottom *= dischargerate;
3808  if (shield.shield8.backrightbottom > min_shield_discharge*shield.shield8.backrightbottommax)
3809  shield.shield8.backrightbottom *= dischargerate;
3810  if (shield.shield8.backleftbottom > min_shield_discharge*shield.shield8.backleftbottommax)
3811  shield.shield8.backleftbottom *= dischargerate;
3812  }
3813  rec = rec*8/shieldenergycap*VSD/100;
3814  break;
3815  }
3816  if (shield.number) {
3817  if (rechargesh == 0)
3818  energy -= rec;
3819  if (shields_require_power)
3820  maxshield = 0;
3821  if (max_shield_lowers_recharge)
3822  energy -= max_shield_lowers_recharge*SIMULATION_ATOM*maxshield*VSD
3823  /( 100*(shield.efficiency ? shield.efficiency : 1) );
3824  if (!max_shield_lowers_capacitance)
3825  maxshield = 0;
3826  }
3827  //Reactor energy
3828  if (energy_before_shield)
3829  RechargeEnergy();
3830  //Final energy computations
3831  float menergy = maxenergy;
3832  if ( shield.number && (menergy-maxshield < low_power_mode) ) {
3833  menergy = maxshield+low_power_mode;
3834  if ( _Universe->isPlayerStarship( this ) ) {
3835  if (rand() < .00005*RAND_MAX) {
3837  0,
3838  " game",
3839  "all",
3840  "**Warning** Power Supply Overdrawn: downgrade shield or purchase reactor capacitance!" );
3841  }
3842  }
3843  }
3844  if (graphicOptions.InWarp) {
3845  //FIXME FIXME FIXME
3846  static float bleedfactor = XMLSupport::parse_float( vs_config->getVariable( "physics", "warpbleed", "20" ) );
3847  float bleed = jump.insysenergy/bleedfactor*SIMULATION_ATOM;
3848  if (warpenergy > bleed) {
3849  warpenergy -= bleed;
3850  } else {
3851  graphicOptions.InWarp = 0;
3852  graphicOptions.WarpRamping = 1;
3853  }
3854  }
3855  float excessenergy = 0;
3856  //NOTE: !shield.number => maxshield==0
3857  if (menergy > maxshield) {
3858  //allow warp caps to absorb xtra power
3859  if (energy > menergy-maxshield) {
3860  excessenergy = energy-(menergy-maxshield);
3861  energy = menergy-maxshield;
3862  if (excessenergy > 0) {
3863  warpenergy = warpenergy+WARPENERGYMULTIPLIER( this )*excessenergy;
3864  float mwe = maxwarpenergy;
3865  if (mwe < jump.energy && mwe == 0)
3866  mwe = jump.energy;
3867  if (warpenergy > mwe) {
3868  excessenergy = (warpenergy-mwe)/WARPENERGYMULTIPLIER( this );
3869  warpenergy = mwe;
3870  }
3871  }
3872  }
3873  } else {
3874  energy = 0;
3875  }
3876  excessenergy = (excessenergy > precharge) ? excessenergy-precharge : 0;
3877  if (reactor_uses_fuel) {
3878  static float min_reactor_efficiency =
3879  XMLSupport::parse_float( vs_config->getVariable( "physics", "min_reactor_efficiency", ".00001" ) );
3880  fuel -= FMEC_factor
3881  *( ( recharge*SIMULATION_ATOM
3882  -(reactor_idle_efficiency
3883  *excessenergy) )/( min_reactor_efficiency+( pImage->LifeSupportFunctionality*(1-min_reactor_efficiency) ) ) );
3884  if (fuel < 0) fuel = 0;
3885  if ( !FINITE( fuel ) ) {
3886  fprintf( stderr, "Fuel is nan C\n" );
3887  fuel = 0;
3888  }
3889  }
3890  energy = energy < 0 ? 0 : energy;
3891 }
3892 
3893 Vector Unit::ResolveForces( const Transformation &trans, const Matrix &transmat )
3894 {
3895  //First, save theoretical instantaneous acceleration (not time-quantized) for GetAcceleration()
3896  SavedAccel = GetNetAcceleration();
3897 
3898  Vector p, q, r;
3899  GetOrientation( p, q, r );
3900  Vector temp1( NetLocalTorque.i*p+NetLocalTorque.j*q+NetLocalTorque.k*r );
3901  if (NetTorque.i || NetTorque.j || NetTorque.k)
3902  temp1 += InvTransformNormal( transmat, NetTorque );
3903  if ( GetMoment() )
3904  temp1 = temp1/GetMoment();
3905  else
3906  VSFileSystem::vs_fprintf( stderr, "zero moment of inertia %s\n", name.get().c_str() );
3907  Vector temp( temp1*SIMULATION_ATOM );
3908  AngularVelocity += temp;
3909  static float maxplayerrotationrate =
3910  XMLSupport::parse_float( vs_config->getVariable( "physics", "maxplayerrot", "24" ) );
3911  static float maxnonplayerrotationrate = XMLSupport::parse_float( vs_config->getVariable( "physics", "maxNPCrot", "360" ) );
3912  float caprate;
3913  if ( _Universe->isPlayerStarship( this ) ) //clamp to avoid vomit-comet effects
3914  caprate = maxplayerrotationrate;
3915  else
3916  caprate = maxnonplayerrotationrate;
3917  if (AngularVelocity.MagnitudeSquared() > caprate*caprate)
3918  AngularVelocity = AngularVelocity.Normalize()*caprate;
3919  //acceleration
3920  Vector temp2 = (NetLocalForce.i*p+NetLocalForce.j*q+NetLocalForce.k*r);
3921  if ( !( FINITE( NetForce.i ) && FINITE( NetForce.j ) && FINITE( NetForce.k ) ) )
3922  cout<<"NetForce skrewed";
3923  if (NetForce.i || NetForce.j || NetForce.k)
3924  temp2 += InvTransformNormal( transmat, NetForce );
3925  temp2 = temp2/GetMass();
3926  temp = temp2*SIMULATION_ATOM;
3927  if ( !( FINITE( temp2.i ) && FINITE( temp2.j ) && FINITE( temp2.k ) ) )
3928  cout<<"NetForce transform skrewed";
3929  float oldmagsquared = Velocity.MagnitudeSquared();
3930  Velocity += temp;
3931  //}
3932 
3933  float newmagsquared = Velocity.MagnitudeSquared();
3934  static float warpstretchcutoff =
3935  XMLSupport::parse_float( vs_config->getVariable( "graphics", "warp_stretch_cutoff",
3936  "500000" ) )*XMLSupport::parse_float(
3937  vs_config->getVariable( "physics", "game_speed", "1" ) );
3938  static float warpstretchoutcutoff =
3939  XMLSupport::parse_float( vs_config->getVariable( "graphics", "warp_stretch_decel_cutoff",
3940  "500000" ) )
3941  *XMLSupport::parse_float( vs_config->getVariable( "physics", "game_speed", "1" ) );
3942  static float cutsqr = warpstretchcutoff*warpstretchcutoff;
3943  static float outcutsqr = warpstretchoutcutoff*warpstretchoutcutoff;
3944  bool oldbig = oldmagsquared > cutsqr;
3945  bool newbig = newmagsquared > cutsqr;
3946  bool oldoutbig = oldmagsquared > outcutsqr;
3947  bool newoutbig = newmagsquared > outcutsqr;
3948  if ( (newbig && !oldbig) || (oldoutbig && !newoutbig) ) {
3949  static string insys_jump_ani = vs_config->getVariable( "graphics", "insys_jump_animation", "warp.ani" );
3950  static bool docache = true;
3951  if (docache) {
3952  UniverseUtil::cacheAnimation( insys_jump_ani );
3953  docache = false;
3954  }
3955  Vector v( GetVelocity() );
3956  v.Normalize();
3957  Vector p, q, r;
3958  GetOrientation( p, q, r );
3959  static float sec =
3960  XMLSupport::parse_float( vs_config->getVariable( "graphics", "insys_jump_ani_second_ahead",
3961  "4" ) )
3962  /( XMLSupport::parse_float( vs_config->getVariable( "physics", "game_speed",
3963  "1" ) )
3964  *XMLSupport::parse_float( vs_config->getVariable( "physics", "game_accel", "1" ) ) );
3965  static float endsec =
3966  XMLSupport::parse_float( vs_config->getVariable( "graphics", "insys_jump_ani_second_ahead_end",
3967  ".03" ) )
3968  /( XMLSupport::parse_float( vs_config->getVariable( "physics", "game_speed",
3969  "1" ) )
3970  *XMLSupport::parse_float( vs_config->getVariable( "physics", "game_accel", "1" ) ) );
3971  float tmpsec = oldbig ? endsec : sec;
3972  UniverseUtil::playAnimationGrow( insys_jump_ani, RealPosition( this ).Cast()+Velocity*tmpsec+v*rSize(), rSize()*8, 1 );
3973  }
3974  static float air_res_coef = XMLSupport::parse_float( active_missions[0]->getVariable( "air_resistance", "0" ) );
3975  static float lateral_air_res_coef = XMLSupport::parse_float( active_missions[0]->getVariable( "lateral_air_resistance", "0" ) );
3976  if (air_res_coef || lateral_air_res_coef) {
3977  float velmag = Velocity.Magnitude();
3978  Vector AirResistance = Velocity
3979  *( air_res_coef*velmag/GetMass() )*(corner_max.i-corner_min.i)*(corner_max.j-corner_min.j);
3980  if (AirResistance.Magnitude() > velmag) {
3981  Velocity.Set( 0, 0, 0 );
3982  } else {
3983  Velocity = Velocity-AirResistance;
3984  if (lateral_air_res_coef) {
3985  Vector p, q, r;
3986  GetOrientation( p, q, r );
3987  Vector lateralVel = p*Velocity.Dot( p )+q*Velocity.Dot( q );
3988  AirResistance = lateralVel
3989  *( lateral_air_res_coef*velmag
3990  /GetMass() )*(corner_max.i-corner_min.i)*(corner_max.j-corner_min.j);
3991  if ( AirResistance.Magnitude() > lateralVel.Magnitude() )
3992  Velocity = r*Velocity.Dot( r );
3993  else
3994  Velocity = Velocity-AirResistance;
3995  }
3996  }
3997  }
3998  NetForce = NetLocalForce = NetTorque = NetLocalTorque = Vector( 0, 0, 0 );
3999 
4000  return temp2;
4001 }
4002 
4004 {
4005  q.Normalize();
4006  r.Normalize();
4007  QVector p;
4008  CrossProduct( q, r, p );
4009  CrossProduct( r, p, q );
4010  curr_physical_state.orientation = Quaternion::from_vectors( p.Cast(), q.Cast(), r.Cast() );
4011 }
4012 
4014 {
4015  q.Normalize();
4016  r.Normalize();
4017  p.Normalize();
4018  curr_physical_state.orientation = Quaternion::from_vectors( p.Cast(), q.Cast(), r.Cast() );
4019 }
4020 
4022 {
4023  curr_physical_state.orientation = Q;
4024 }
4025 
4026 #define MM( A, B ) m.r[B*3+A]
4027 
4029 {
4030  Matrix m;
4031  curr_physical_state.to_matrix( m );
4032  return Vector( v.i*MM( 0, 0 )+v.j*MM( 1, 0 )+v.k*MM( 2, 0 ),
4033  v.i*MM( 0, 1 )+v.j*MM( 1, 1 )+v.k*MM( 2, 1 ),
4034  v.i*MM( 0, 2 )+v.j*MM( 1, 2 )+v.k*MM( 2, 2 ) );
4035 }
4036 
4037 #undef MM
4038 
4040 {
4041  Matrix m;
4042  curr_physical_state.to_matrix( m );
4043  return TransformNormal( m, v );
4044 }
4045 
4046 #define MM( A, B ) ( (cumulative_transformation_matrix.r[B*3+A]) )
4047 
4049 {
4050  //Matrix m;
4051  //062201: not a cumulative transformation...in prev unit space curr_physical_state.to_matrix(m);
4052  return Vector( v.i*MM( 0, 0 )+v.j*MM( 1, 0 )+v.k*MM( 2, 0 ),
4053  v.i*MM( 0, 1 )+v.j*MM( 1, 1 )+v.k*MM( 2, 1 ),
4054  v.i*MM( 0, 2 )+v.j*MM( 1, 2 )+v.k*MM( 2, 2 ) );
4055 }
4056 
4057 #undef MM
4058 
4060 {
4061  return TransformNormal( cumulative_transformation_matrix, v );
4062 }
4063 
4064 /*
4065  **********************************************************************************
4066  **** UNIT_DAMAGE STUFF
4067  **********************************************************************************
4068  */
4069 
4070 void Unit::LightShields( const Vector &pnt, const Vector &normal, float amt, const GFXColor &color )
4071 {
4072  meshdata.back()->AddDamageFX( pnt, shieldtight ? shieldtight*normal : Vector( 0, 0, 0 ), mymin( 1.0f, mymax( 0.0f,
4073  amt ) ), color );
4074 }
4075 
4076 //NEW TESTING MODIFICATIONS
4077 //We do it also on client side to display hits on shields/armor -> not to compute damage
4078 //Damage are computed on server side and shield/armor data are sent with the DAMAGE SNAPSHOT
4079 float Unit::ApplyLocalDamage( const Vector &pnt,
4080  const Vector &normal,
4081  float amt,
4082  Unit *affectedUnit,
4083  const GFXColor &color,
4084  float phasedamage )
4085 {
4086  static float nebshields = XMLSupport::parse_float( vs_config->getVariable( "physics", "nebula_shield_recharge", ".5" ) );
4087  Cockpit *cpt;
4088  if ( ( cpt = _Universe->isPlayerStarship( this ) ) != NULL ) {
4089  if (color.a != 2) {
4090  static bool apply_difficulty_enemy_damage =
4091  XMLSupport::parse_bool( vs_config->getVariable( "physics", "difficulty_based_enemy_damage", "true" ) );
4092  if (apply_difficulty_enemy_damage) {
4093  phasedamage *= g_game.difficulty;
4094  amt *= g_game.difficulty;
4095  }
4096  }
4097  }
4098  float absamt = amt >= 0 ? amt : -amt;
4099  float ppercentage = 0;
4100  //We also do the following lock on client side in order not to display shield hits
4101  static bool nodockdamage = XMLSupport::parse_float( vs_config->getVariable( "physics", "no_damage_to_docked_ships", "true" ) );
4102  if (nodockdamage)
4103  if ( DockedOrDocking()&(DOCKED_INSIDE|DOCKED) )
4104  return -1;
4105  if (affectedUnit != this) {
4106  affectedUnit->ApplyLocalDamage( pnt, normal, amt, affectedUnit, color, phasedamage );
4107  return -1;
4108  }
4109  if (aistate)
4110  aistate->ChooseTarget();
4111  float leakamt = phasedamage+amt*.01*shield.leak;
4112  amt *= 1-.01*shield.leak;
4113  //Percentage returned by DealDamageToShield
4114  float spercentage = 0;
4115  //If not a nebula or if shields recharge in nebula => WE COMPUTE SHIELDS DAMAGE AND APPLY
4116  if ( GetNebula() == NULL || (nebshields > 0) ) {
4117  float origabsamt = absamt;
4118  spercentage = DealDamageToShield( pnt, absamt );
4119 
4120  amt = amt >= 0 ? absamt : -absamt;
4121  //shields are up
4122  if ( meshdata.back() && spercentage > 0 && (origabsamt-absamt > shield.recharge || amt == 0) ) {
4123  //calculate percentage
4124  if (cpt)
4125  cpt->Shake( amt, 0 );
4126  if (GetNebula() == NULL)
4127  LightShields( pnt, normal, spercentage, color );
4128  }
4129  }
4130  //If shields failing or... => WE COMPUTE DAMAGE TO HULL
4131  if (shield.leak > 0 || !meshdata.back() || spercentage == 0 || absamt > 0 || phasedamage) {
4132  float tmp = this->GetHull();
4133  ppercentage = DealDamageToHull( pnt, leakamt+amt );
4134  if (cpt)
4135  cpt->Shake( amt+leakamt, tmp != this->GetHull() ? 2 : 1 );
4136  if (ppercentage != -1) {
4137  //returns -1 on death--could delete
4138  for (int i = 0; i < nummesh(); ++i)
4139  if (ppercentage)
4140  meshdata[i]->AddDamageFX( pnt, shieldtight ? shieldtight*normal : Vector( 0, 0, 0 ), ppercentage, color );
4141  }
4142  }
4143  //If server and there is damage to shields or if unit is not killed (ppercentage>0)
4144  if ( SERVER && (ppercentage > 0 || spercentage > 0) ) {
4145 #if 1 //def NET_SHIELD_SYSTEM_1
4146  //FIRST METHOD : send each time a unit is hit all the damage info to all clients in the current zone
4147  //If server side, we send the unit serial + serialized shields + shield recharge + shield leak + ...
4148  Vector netpnt = pnt;
4149  Vector netnormal = normal;
4150  GFXColor col( color.r, color.g, color.b, color.a );
4151  VSServer->sendDamages( this->serial,
4152  this->getStarSystem()->GetZone(), hull, shield, armor, ppercentage, spercentage, amt, netpnt,
4153  netnormal, col );
4154  //This way the client computes damages based on what we send to him => less reliable
4155 #endif
4156 #if 1
4157  //SECOND METHOD : we just put a flag on the unit telling its shield/armor data has changed
4158  if (spercentage > 0)
4159  this->damages |= SHIELD_DAMAGED;
4160  if (ppercentage > 0)
4161  this->damages |= ARMOR_DAMAGED;
4162 #endif
4163  }
4164  return ppercentage > 0 ? 2.0f : 1.0f;
4165 }
4166 
4167 void Unit::ApplyNetDamage( Vector &pnt, Vector &normal, float amt, float ppercentage, float spercentage, GFXColor &color )
4168 {
4169  static float nebshields = XMLSupport::parse_float( vs_config->getVariable( "physics", "nebula_shield_recharge", ".5" ) );
4170  Cockpit *cpt;
4171  if ( ( cpt = _Universe->isPlayerStarship( this ) ) != NULL ) {}
4172  if (GetNebula() == NULL || nebshields > 0) {
4173  //shields are up
4174  if (meshdata.back() && spercentage > 0 && amt == 0) {
4175  if (GetNebula() == NULL)
4176  meshdata.back()->AddDamageFX( pnt, shieldtight ? shieldtight*normal : Vector( 0, 0, 0 ), spercentage, color );
4177  if (cpt)
4178  cpt->Shake( amt, 0 );
4179  }
4180  }
4181  if (shield.leak > 0 || !meshdata.back() || spercentage == 0 || amt > 0) {
4182  if (ppercentage != -1) {
4183  //returns -1 on death--could delete
4184  for (int i = 0; i < nummesh(); ++i)
4185  if (ppercentage) {
4186  meshdata[i]->AddDamageFX( pnt, shieldtight ? shieldtight*normal : Vector( 0, 0, 0 ), ppercentage, color );
4187  if (cpt)
4188  cpt->Shake( amt, 2 );
4189  }
4190  }
4191  }
4192 }
4193 
4194 Unit * findUnitInStarsystem( void *unitDoNotDereference )
4195 {
4196  Unit *un;
4197  for (un_kiter i = _Universe->activeStarSystem()->getUnitList().constIterator(); (un = *i) != NULL; ++i)
4198  if (un == unitDoNotDereference)
4199  return un;
4200  return NULL;
4201 }
4202 
4203 extern void ScoreKill( Cockpit *cp, Unit *killer, Unit *killedUnit );
4204 //Changed order of things -> Vectors and ApplyLocalDamage are computed before Cockpit thing now
4205 void AllUnitsCloseAndEngage( Unit*, int faction );
4206 void Unit::ApplyDamage( const Vector &pnt,
4207  const Vector &normal,
4208  float amt,
4209  Unit *affectedUnit,
4210  const GFXColor &color,
4211  void *ownerDoNotDereference,
4212  float phasedamage )
4213 {
4214  Cockpit *cp = _Universe->isPlayerStarshipVoid( ownerDoNotDereference );
4215  float hullpercent = GetHullPercent();
4216  //Only on client side
4217  bool mykilled = hull < 0;
4218  Vector localpnt( InvTransform( cumulative_transformation_matrix, pnt ) );
4219  Vector localnorm( ToLocalCoordinates( normal ) );
4220  //Only call when on servre side or non-networking
4221  //If networking damages are applied as they are received
4222  static float hull_percent_for_comm = XMLSupport::parse_float( vs_config->getVariable( "AI", "HullPercentForComm", ".75" ) );
4223  bool armor_damage = false;
4224  if (SERVER || Network == NULL)
4225  armor_damage = (ApplyLocalDamage( localpnt, localnorm, amt, affectedUnit, color, phasedamage ) == 2);
4226  if (!Network && cp) {
4227  static int MadnessForShieldDamage = XMLSupport::parse_bool( vs_config->getVariable( "AI", "ShieldDamageAnger", "1" ) );
4228  static int MadnessForHullDamage = XMLSupport::parse_bool( vs_config->getVariable( "AI", "HullDamageAnger", "10" ) );
4229  int howmany = armor_damage ? MadnessForHullDamage : MadnessForShieldDamage;
4230  for (int i = 0; i < howmany; ++i) {
4231  //now we can dereference it because we checked it against the parent
4232  CommunicationMessage c( reinterpret_cast< Unit* > (ownerDoNotDereference), this, NULL, 0 );
4233  c.SetCurrentState( c.fsm->GetHitNode(), NULL, 0 );
4234  if ( this->getAIState() ) this->getAIState()->Communicate( c );
4235  }
4236  //the dark danger is real!
4237  Threaten( reinterpret_cast< Unit* > (ownerDoNotDereference), 10 );
4238  } else if (!Network) {
4239  //if only the damage contained which faction it belonged to
4240  pilot->DoHit( this, ownerDoNotDereference, FactionUtil::GetNeutralFaction() );
4241  }
4242  if (hull < 0) {
4243  ClearMounts();
4244  if (!mykilled) {
4245  if (cp) {
4246  ScoreKill( cp, reinterpret_cast< Unit* > (ownerDoNotDereference), this );
4247  } else {
4248  Unit *tmp;
4249  if ( ( tmp = findUnitInStarsystem( ownerDoNotDereference ) ) != NULL ) {
4250  if ( ( NULL != ( cp = _Universe->isPlayerStarshipVoid( tmp->owner ) ) )
4251  && (cp->GetParent() != NULL) )
4252  ScoreKill( cp, cp->GetParent(), this );
4253  else
4254  ScoreKill( NULL, tmp, this );
4255  }
4256  }
4257  }
4258  } else if ( hullpercent >= hull_percent_for_comm && ( (float) GetHullPercent() ) < hull_percent_for_comm
4259  && ( cp || _Universe->isPlayerStarship( this ) ) ) {
4260  Unit *computerai = NULL;
4261  Unit *player = NULL;
4262  if (cp == NULL) {
4263  computerai = findUnitInStarsystem( ownerDoNotDereference );
4264  player = this;
4265  } else {
4266  computerai = this;
4267  //cp != NULL
4268  player = cp->GetParent();
4269  }
4270  if (computerai && player && computerai->getAIState() && player->getAIState() && computerai->isUnit() == UNITPTR
4271  && player->isUnit() == UNITPTR) {
4272  unsigned char gender;
4273  vector< Animation* > *anim = computerai->pilot->getCommFaces( gender );
4274  if (cp) {
4275  static bool assistallyinneed =
4276  XMLSupport::parse_bool( vs_config->getVariable( "AI", "assist_friend_in_need", "true" ) );
4277  if (assistallyinneed)
4278  AllUnitsCloseAndEngage( player, computerai->faction );
4279  }
4280  if (GetHullPercent() > 0 || !cp) {
4281  CommunicationMessage c( computerai, player, anim, gender );
4282  c.SetCurrentState( cp ? c.fsm->GetDamagedNode() : c.fsm->GetDealtDamageNode(), anim, gender );
4283  player->getAIState()->Communicate( c );
4284  }
4285  }
4286  }
4287 }
4288 
4289 //NUMGAUGES has been moved to pImages.h in UnitImages<void>
4290 void Unit::DamageRandSys( float dam, const Vector &vec, float randnum, float degrees )
4291 {
4292  float deg = fabs( 180*atan2( vec.i, vec.k )/M_PI );
4293  randnum = rand01();
4294  static float inv_min_dam = 1.0f-XMLSupport::parse_float( vs_config->getVariable( "physics", "min_damage", ".001" ) );
4295  static float inv_max_dam = 1.0f-XMLSupport::parse_float( vs_config->getVariable( "physics", "min_damage", ".999" ) );
4296  if (dam < inv_max_dam) dam = inv_max_dam;
4297  if (dam > inv_min_dam) dam = inv_min_dam;
4298  degrees = deg;
4299  if (degrees > 180)
4300  degrees = 360-degrees;
4301  if (degrees >= 0 && degrees < 20) {
4302  int which = rand()%(1+UnitImages< void >::NUMGAUGES+MAXVDUS);
4303  pImage->cockpit_damage[which] *= dam;
4304  if (pImage->cockpit_damage[which] < .1)
4305  pImage->cockpit_damage[which] = 0;
4306  //DAMAGE COCKPIT
4307  if (randnum >= .85) {//do 25% damage to a gauge
4308  pImage->cockpit_damage[which] *= .75;
4309  if (pImage->cockpit_damage[which] < .1)
4310  pImage->cockpit_damage[which] = 0;
4311  } else if (randnum >= .775) {
4312  computer.itts = false; //Set the computer to not have an itts
4313  } else if (randnum >= .7) {
4314  // Gradually degrade radar capabilities
4315  typedef Computer::RADARLIM::Capability Capability;
4316  int& capability = computer.radar.capability;
4317  if (capability & Capability::IFF_THREAT_ASSESSMENT)
4318  {
4319  capability &= ~Capability::IFF_THREAT_ASSESSMENT;
4320  }
4321  else if (capability & Capability::IFF_OBJECT_RECOGNITION)
4322  {
4323  capability &= ~Capability::IFF_OBJECT_RECOGNITION;
4324  }
4325  else if (capability & Capability::IFF_FRIEND_FOE)
4326  {
4327  capability &= ~Capability::IFF_FRIEND_FOE;
4328  }
4329  } else if (randnum >= .5) {
4330  //THIS IS NOT YET SUPPORTED IN NETWORKING
4331  computer.target = NULL; //set the target to NULL
4332  } else if (randnum >= .4) {
4333  limits.retro *= dam;
4334  } else if (randnum >= .3275) {
4335  static float maxdam = XMLSupport::parse_float( vs_config->getVariable( "physics", "max_radar_cone_damage", ".9" ) );
4336  computer.radar.maxcone += (1-dam);
4337  if (computer.radar.maxcone > maxdam)
4338  computer.radar.maxcone = maxdam;
4339  } else if (randnum >= .325) {
4340  static float maxdam =
4341  XMLSupport::parse_float( vs_config->getVariable( "physics", "max_radar_lockcone_damage", ".95" ) );
4342  computer.radar.lockcone += (1-dam);
4343  if (computer.radar.lockcone > maxdam)
4344  computer.radar.lockcone = maxdam;
4345  } else if (randnum >= .25) {
4346  static float maxdam =
4347  XMLSupport::parse_float( vs_config->getVariable( "physics", "max_radar_trackcone_damage", ".98" ) );
4348  computer.radar.trackingcone += (1-dam);
4349  if (computer.radar.trackingcone > maxdam)
4350  computer.radar.trackingcone = maxdam;
4351  } else if (randnum >= .175) {
4352  computer.radar.maxrange *= dam;
4353  } else {
4354  int which = rand()%(1+UnitImages< void >::NUMGAUGES+MAXVDUS);
4355  pImage->cockpit_damage[which] *= dam;
4356  if (pImage->cockpit_damage[which] < .1)
4357  pImage->cockpit_damage[which] = 0;
4358  }
4359  damages |= COMPUTER_DAMAGED;
4360  return;
4361  }
4362  static float thruster_hit_chance = XMLSupport::parse_float( vs_config->getVariable( "physics", "thruster_hit_chance", ".25" ) );
4363  if (rand01() < thruster_hit_chance) {
4364  //DAMAGE ROLL/YAW/PITCH/THRUST
4365  float orandnum = rand01()*.82+.18;
4366  if (randnum >= .9)
4367  computer.max_pitch_up *= orandnum;
4368  else if (randnum >= .8)
4369  computer.max_yaw_right *= orandnum;
4370  else if (randnum >= .6)
4371  computer.max_yaw_left *= orandnum;
4372  else if (randnum >= .4)
4373  computer.max_pitch_down *= orandnum;
4374  else if (randnum >= .2)
4375  computer.max_roll_right *= orandnum;
4376  else if (randnum >= .18)
4377  computer.max_roll_left *= orandnum;
4378  else if (randnum >= .17)
4379  limits.roll *= dam;
4380  else if (randnum >= .10)
4381  limits.yaw *= dam;
4382  else if (randnum >= .03)
4383  limits.pitch *= dam;
4384  else
4385  limits.lateral *= dam;
4386  damages |= LIMITS_DAMAGED;
4387  return;
4388  }
4389  if (degrees >= 20 && degrees < 35) {
4390  //DAMAGE MOUNT
4391  if (randnum >= .65 && randnum < .9) {
4392  pImage->ecm *= float_to_int( dam );
4393  } else if ( GetNumMounts() ) {
4394  unsigned int whichmount = rand()%GetNumMounts();
4395  if (randnum >= .9)
4396  DestroyMount( &mounts[whichmount] );
4397  else if (mounts[whichmount].ammo > 0 && randnum >= .75)
4398  mounts[whichmount].ammo *= float_to_int( dam );
4399  else if (randnum >= .7)
4400  mounts[whichmount].time_to_lock += ( 100-(100*dam) );
4401  else if (randnum >= .2)
4402  mounts[whichmount].functionality *= dam;
4403  else
4404  mounts[whichmount].maxfunctionality *= dam;
4405  }
4406  damages |= MOUNT_DAMAGED;
4407  return;
4408  }
4409  if (degrees >= 35 && degrees < 60) {
4410  //DAMAGE FUEL
4411  static float fuel_damage_prob = 1.f
4412  -XMLSupport::parse_float( vs_config->getVariable( "physics", "fuel_damage_prob", ".25" ) );
4413  static float warpenergy_damage_prob = fuel_damage_prob
4415  "warpenergy_damage_prob",
4416  "0.05" ) );
4417  static float ab_damage_prob = warpenergy_damage_prob
4418  -XMLSupport::parse_float( vs_config->getVariable( "physics", "ab_damage_prob", ".2" ) );
4419  static float cargovolume_damage_prob = ab_damage_prob
4421  "cargovolume_damage_prob",
4422  ".15" ) );
4423  static float upgradevolume_damage_prob = cargovolume_damage_prob
4425  "upgradevolume_damage_prob",
4426  ".1" ) );
4427  static float cargo_damage_prob = upgradevolume_damage_prob
4428  -XMLSupport::parse_float( vs_config->getVariable( "physics", "cargo_damage_prob", "1" ) );
4429  if (randnum >= fuel_damage_prob) {
4430  fuel *= dam;
4431  } else if (randnum >= warpenergy_damage_prob) {
4432  warpenergy *= dam;
4433  } else if (randnum >= ab_damage_prob) {
4434  this->afterburnenergy += ( (1-dam)*recharge );
4435  } else if (randnum >= cargovolume_damage_prob) {
4436  pImage->CargoVolume *= dam;
4437  } else if (randnum >= upgradevolume_damage_prob) {
4438  pImage->UpgradeVolume *= dam;
4439  } else if (randnum >= cargo_damage_prob) {
4440  //Do something NASTY to the cargo
4441  if (pImage->cargo.size() > 0) {
4442  unsigned int i = 0;
4443  unsigned int cargorand_o = rand();
4444  unsigned int cargorand;
4445  do
4446  cargorand = (cargorand_o+i)%pImage->cargo.size();
4447  while ( (pImage->cargo[cargorand].quantity == 0
4448  || pImage->cargo[cargorand].mission) && (++i) < pImage->cargo.size() );
4449  pImage->cargo[cargorand].quantity *= float_to_int( dam );
4450  }
4451  }
4452  damages |= CARGOFUEL_DAMAGED;
4453  return;
4454  }
4455  if (degrees >= 90 && degrees < 120) {
4456  //DAMAGE Shield
4457  //DAMAGE cloak
4458  if (randnum >= .95) {
4459  this->cloaking = -1;
4460  damages |= CLOAK_DAMAGED;
4461  } else if (randnum >= .78) {
4462  pImage->cloakenergy += ( (1-dam)*recharge );
4463  damages |= CLOAK_DAMAGED;
4464  } else if (randnum >= .7) {
4465  cloakmin += ( rand()%(32000-cloakmin) );
4466  damages |= CLOAK_DAMAGED;
4467  }
4468  switch (shield.number)
4469  {
4470  case 2:
4471  if (randnum >= .25 && randnum < .75)
4472  shield.shield2fb.frontmax *= dam;
4473  else
4474  shield.shield2fb.backmax *= dam;
4475  break;
4476  case 4:
4477  if (randnum >= .5 && randnum < .75)
4478  shield.shield4fbrl.frontmax *= dam;
4479  else if (randnum >= .75)
4480  shield.shield4fbrl.backmax *= dam;
4481  else if (randnum >= .25)
4482  shield.shield4fbrl.leftmax *= dam;
4483  else
4484  shield.shield4fbrl.rightmax *= dam;
4485  break;
4486  case 8:
4487  if (randnum < .125)
4488  shield.shield8.frontrighttopmax *= dam;
4489  else if (randnum < .25)
4490  shield.shield8.backrighttopmax *= dam;
4491  else if (randnum < .375)
4492  shield.shield8.frontlefttopmax *= dam;
4493  else if (randnum < .5)
4494  shield.shield8.backrighttopmax *= dam;
4495  else if (randnum < .625)
4496  shield.shield8.frontrightbottommax *= dam;
4497  else if (randnum < .75)
4498  shield.shield8.backrightbottommax *= dam;
4499  else if (randnum < .875)
4500  shield.shield8.frontleftbottommax *= dam;
4501  else
4502  shield.shield8.backrightbottommax *= dam;
4503  break;
4504  }
4505  damages |= SHIELD_DAMAGED;
4506  return;
4507  }
4508  if (degrees >= 120 && degrees < 150) {
4509  //DAMAGE Reactor
4510  //DAMAGE JUMP
4511  if (randnum >= .9) {
4512  static char max_shield_leak =
4513  (char) mymax( 0.0,
4514  mymin( 100.0, XMLSupport::parse_float( vs_config->getVariable( "physics", "max_shield_leak", "90" ) ) ) );
4515  static char min_shield_leak =
4516  (char) mymax( 0.0,
4517  mymin( 100.0, XMLSupport::parse_float( vs_config->getVariable( "physics", "max_shield_leak", "0" ) ) ) );
4518  char newleak = float_to_int( mymax( min_shield_leak, mymax( max_shield_leak, (char) ( (randnum-.9)*10.0*100.0 ) ) ) );
4519  if (shield.leak < newleak)
4520  shield.leak = newleak;
4521  } else if (randnum >= .7) {
4522  shield.recharge *= dam;
4523  } else if (randnum >= .5) {
4524  static float mindam = XMLSupport::parse_float( vs_config->getVariable( "physics", "min_recharge_shot_damage", "0.5" ) );
4525  if (dam < mindam)
4526  dam = mindam;
4527  this->recharge *= dam;
4528  } else if (randnum >= .2) {
4529  static float mindam =
4530  XMLSupport::parse_float( vs_config->getVariable( "physics", "min_maxenergy_shot_damage", "0.2" ) );
4531  if (dam < mindam)
4532  dam = mindam;
4533  this->maxenergy *= dam;
4534  } else if (pImage->repair_droid > 0) {
4535  pImage->repair_droid--;
4536  }
4537  damages |= JUMP_DAMAGED;
4538  return;
4539  }
4540  if (degrees >= 150 && degrees <= 180) {
4541  //DAMAGE ENGINES
4542  if (randnum >= .8)
4543  computer.max_combat_ab_speed *= dam;
4544  else if (randnum >= .6)
4545  computer.max_combat_speed *= dam;
4546  else if (randnum >= .4)
4547  limits.afterburn *= dam;
4548  else if (randnum >= .2)
4549  limits.vertical *= dam;
4550  else
4551  limits.forward *= dam;
4552  damages |= LIMITS_DAMAGED;
4553  return;
4554  }
4555 }
4556 
4557 void Unit::Kill( bool erasefromsave, bool quitting )
4558 {
4559  if (this->colTrees)
4560  this->colTrees->Dec(); //might delete
4561  this->colTrees = NULL;
4562  if (this->sound->engine != -1) {
4563  AUDStopPlaying( this->sound->engine );
4564  AUDDeleteSound( this->sound->engine );
4565  }
4566  if (this->sound->explode != -1) {
4567  AUDStopPlaying( this->sound->explode );
4568  AUDDeleteSound( this->sound->explode );
4569  }
4570  if (this->sound->shield != -1) {
4571  AUDStopPlaying( this->sound->shield );
4572  AUDDeleteSound( this->sound->shield );
4573  }
4574  if (this->sound->armor != -1) {
4575  AUDStopPlaying( this->sound->armor );
4576  AUDDeleteSound( this->sound->armor );
4577  }
4578  if (this->sound->hull != -1) {
4579  AUDStopPlaying( this->sound->hull );
4580  AUDDeleteSound( this->sound->hull );
4581  }
4582  if (this->sound->cloak != -1) {
4583  AUDStopPlaying( this->sound->cloak );
4584  AUDDeleteSound( this->sound->cloak );
4585  }
4586  ClearMounts();
4587  if (SERVER && this->serial) {
4588  VSServer->sendKill( this->serial, this->getStarSystem()->GetZone() );
4589  this->serial = 0;
4590  }
4591  if ( docked&(DOCKING_UNITS) ) {
4592  static float survival =
4593  XMLSupport::parse_float( vs_config->getVariable( "physics", "survival_chance_on_base_death", "0.1" ) );
4594  static float player_survival =
4595  XMLSupport::parse_float( vs_config->getVariable( "physics", "player_survival_chance_on_base_death", "1.0" ) );
4596  static int i_survival = float_to_int( (RAND_MAX*survival) );
4597  static int i_player_survival = float_to_int( (RAND_MAX*player_survival) );
4598 
4599  vector< Unit* >dockedun;
4600  unsigned int i;
4601  for (i = 0; i < pImage->dockedunits.size(); ++i) {
4602  Unit *un;
4603  if ( NULL != ( un = pImage->dockedunits[i]->uc.GetUnit() ) )
4604  dockedun.push_back( un );
4605  }
4606  while ( !dockedun.empty() ) {
4607  if (Network) _Universe->netLock( true );
4608  dockedun.back()->UnDock( this );
4609  if (Network) _Universe->netLock( false );
4610  if ( rand() <= (UnitUtil::isPlayerStarship( dockedun.back() ) ? i_player_survival : i_survival) )
4611  dockedun.back()->Kill();
4612  dockedun.pop_back();
4613  }
4614  }
4615  //eraticate everything. naturally (see previous line) we won't erraticate beams erraticated above
4616  if ( !isSubUnit() )
4617  RemoveFromSystem();
4618  killed = true;
4619  computer.target.SetUnit( NULL );
4620 
4621  //God I can't believe this next line cost me 1 GIG of memory until I added it
4622  computer.threat.SetUnit( NULL );
4623  computer.velocity_ref.SetUnit( NULL );
4624  computer.force_velocity_ref = true;
4625  if (aistate) {
4626  aistate->ClearMessages();
4627  aistate->Destroy();
4628  }
4629  aistate = NULL;
4630  Unit *un;
4631  for (un_iter iter = getSubUnits(); (un = *iter); ++iter)
4632  un->Kill();
4633 
4634  if (isUnit() != MISSILEPTR)
4635  VSFileSystem::vs_dprintf( 1, "UNIT HAS DIED: %s %s (file %s)\n", name.get().c_str(),
4636  fullname.c_str(), filename.get().c_str() );
4637 
4638  if (ucref == 0) {
4639  Unitdeletequeue.push_back( this );
4640  if (flightgroup)
4641  if (flightgroup->leader.GetUnit() == this)
4642  flightgroup->leader.SetUnit( NULL );
4643 
4644 #ifdef DESTRUCTDEBUG
4645  VSFileSystem::vs_dprintf( 3, "%s 0x%x - %d\n", name.c_str(), this, Unitdeletequeue.size() );
4646 #endif
4647  }
4648 }
4649 
4650 void Unit::leach( float damShield, float damShieldRecharge, float damEnRecharge )
4651 {
4652  recharge *= damEnRecharge;
4653  shield.recharge *= damShieldRecharge;
4654  switch (shield.number)
4655  {
4656  case 2:
4657  shield.shield2fb.frontmax *= damShield;
4658  shield.shield2fb.backmax *= damShield;
4659  break;
4660  case 4:
4661  shield.shield4fbrl.frontmax *= damShield;
4662  shield.shield4fbrl.backmax *= damShield;
4663  shield.shield4fbrl.leftmax *= damShield;
4664  shield.shield4fbrl.rightmax *= damShield;
4665  break;
4666  case 8:
4667  shield.shield8.frontrighttopmax *= damShield;
4668  shield.shield8.backrighttopmax *= damShield;
4669  shield.shield8.frontlefttopmax *= damShield;
4670  shield.shield8.backlefttopmax *= damShield;
4671  shield.shield8.frontrightbottommax *= damShield;
4672  shield.shield8.backrightbottommax *= damShield;
4673  shield.shield8.frontleftbottommax *= damShield;
4674  shield.shield8.backleftbottommax *= damShield;
4675  break;
4676  }
4677 }
4678 
4679 void Unit::UnRef()
4680 {
4681 #ifdef CONTAINER_DEBUG
4682  CheckUnit( this );
4683 #endif
4684  ucref--;
4685  if (killed && ucref == 0) {
4686 #ifdef CONTAINER_DEBUG
4687  deletedUn.Put( (long) this, this );
4688 #endif
4689  //delete
4690  Unitdeletequeue.push_back( this );
4691 #ifdef DESTRUCTDEBUG
4692  VSFileSystem::vs_fprintf( stderr, "%s 0x%x - %d\n", name.c_str(), this, Unitdeletequeue.size() );
4693 #endif
4694  }
4695 }
4696 
4698 {
4699  static float expsize = XMLSupport::parse_float( vs_config->getVariable( "graphics", "explosion_size", "3" ) );
4700  return expsize*rSize();
4701 }
4702 
4703 //short fix
4704 void Unit::ArmorData( float armor[8] ) const
4705 {
4706  armor[0] = this->armor.frontrighttop;
4707  armor[1] = this->armor.backrighttop;
4708  armor[2] = this->armor.frontlefttop;
4709  armor[3] = this->armor.backlefttop;
4710  armor[4] = this->armor.frontrightbottom;
4711  armor[5] = this->armor.backrightbottom;
4712  armor[6] = this->armor.frontleftbottom;
4713  armor[7] = this->armor.backleftbottom;
4714 }
4715 
4716 float Unit::WarpCapData() const
4717 {
4718  return maxwarpenergy;
4719 }
4720 
4721 float Unit::FuelData() const
4722 {
4723  return fuel;
4724 }
4725 
4727 {
4728  if (maxwarpenergy > 0)
4729  return ( (float) warpenergy )/( (float) maxwarpenergy );
4730  if (jump.energy > 0)
4731  return ( (float) warpenergy )/( (float) jump.energy );
4732  return 0.0f;
4733 }
4734 
4735 float Unit::EnergyData() const
4736 {
4737  static bool max_shield_lowers_capacitance =
4738  XMLSupport::parse_bool( vs_config->getVariable( "physics", "max_shield_lowers_capacitance", "false" ) );
4739  if (max_shield_lowers_capacitance) {
4740  if ( maxenergy <= totalShieldEnergyCapacitance( shield ) )
4741  return 0;
4742  return ( (float) energy )/( maxenergy-totalShieldEnergyCapacitance( shield ) );
4743  } else {
4744  return ( (float) energy )/maxenergy;
4745  }
4746 }
4747 
4748 float Unit::FShieldData() const
4749 {
4750  switch (shield.number)
4751  {
4752  case 2:
4753  {
4754  if (shield.shield2fb.frontmax != 0)
4755  return shield.shield2fb.front/shield.shield2fb.frontmax;
4756  break;
4757  }
4758  case 4:
4759  {
4760  if (shield.shield4fbrl.frontmax != 0)
4761  return (shield.shield4fbrl.front)/shield.shield4fbrl.frontmax;
4762  break;
4763  }
4764  case 8:
4765  {
4766  if (shield.shield8.frontrighttopmax != 0 || shield.shield8.frontrightbottommax != 0
4767  || shield.shield8.frontlefttopmax != 0 || shield.shield8.frontleftbottommax
4768  != 0) {
4769  return (shield.shield8.frontrighttop+shield.shield8.frontrightbottom+shield.shield8.frontlefttop
4770  +shield.shield8.frontleftbottom)
4771  /(shield.shield8.frontrighttopmax+shield.shield8.frontrightbottommax+shield.shield8.frontlefttopmax
4772  +shield.shield8.frontleftbottommax);
4773  }
4774  break;
4775  }
4776  }
4777  return 0;
4778 }
4779 
4780 float Unit::BShieldData() const
4781 {
4782  switch (shield.number)
4783  {
4784  case 2:
4785  {
4786  if (shield.shield2fb.backmax != 0)
4787  return shield.shield2fb.back/shield.shield2fb.backmax;
4788  break;
4789  }
4790  case 4:
4791  {
4792  if (shield.shield4fbrl.backmax != 0)
4793  return (shield.shield4fbrl.back)/shield.shield4fbrl.backmax;
4794  break;
4795  }
4796  case 8:
4797  {
4798  if (shield.shield8.backrighttopmax != 0 || shield.shield8.backrightbottommax != 0
4799  || shield.shield8.backlefttopmax != 0 || shield.shield8.backleftbottommax
4800  != 0) {
4801  return (shield.shield8.backrighttop+shield.shield8.backrightbottom+shield.shield8.backlefttop
4802  +shield.shield8.backleftbottom)
4803  /(shield.shield8.backrighttopmax+shield.shield8.backrightbottommax+shield.shield8.backlefttopmax
4804  +shield.shield8.backleftbottommax);
4805  }
4806  break;
4807  }
4808  }
4809  return 0;
4810 }
4811 
4812 float Unit::LShieldData() const
4813 {
4814  switch (shield.number)
4815  {
4816  case 2:
4817  return 0; //no data, captain
4818 
4819  case 4:
4820  {
4821  if (shield.shield4fbrl.leftmax != 0)
4822  return (shield.shield4fbrl.left)/shield.shield4fbrl.leftmax;
4823  break;
4824  }
4825  case 8:
4826  {
4827  if (shield.shield8.backlefttopmax != 0 || shield.shield8.backleftbottommax != 0
4828  || shield.shield8.frontlefttopmax != 0 || shield.shield8.frontleftbottommax
4829  != 0) {
4830  return (shield.shield8.backlefttop+shield.shield8.backleftbottom+shield.shield8.frontlefttop
4831  +shield.shield8.frontleftbottom)
4832  /(shield.shield8.backlefttopmax+shield.shield8.backleftbottommax+shield.shield8.frontlefttopmax
4833  +shield.shield8.frontleftbottommax);
4834  }
4835  break;
4836  }
4837  }
4838  return 0;
4839 }
4840 
4841 float Unit::RShieldData() const
4842 {
4843  switch (shield.number)
4844  {
4845  case 2:
4846  return 0; //don't react to stuff we have no data on
4847 
4848  case 4:
4849  {
4850  if (shield.shield4fbrl.rightmax != 0)
4851  return (shield.shield4fbrl.right)/shield.shield4fbrl.rightmax;
4852  break;
4853  }
4854  case 8:
4855  {
4856  if (shield.shield8.backrighttopmax != 0 || shield.shield8.backrightbottommax != 0
4857  || shield.shield8.frontrighttopmax != 0 || shield.shield8.frontrightbottommax
4858  != 0) {
4859  return (shield.shield8.backrighttop+shield.shield8.backrightbottom+shield.shield8.frontrighttop
4860  +shield.shield8.frontrightbottom)
4861  /(shield.shield8.backrighttopmax+shield.shield8.backrightbottommax+shield.shield8.frontrighttopmax
4862  +shield.shield8.frontrightbottommax);
4863  }
4864  break;
4865  }
4866  }
4867  return 0;
4868 }
4869 
4871 {
4872  while ( !Unitdeletequeue.empty() ) {
4873 #ifdef DESTRUCTDEBUG
4874  VSFileSystem::vs_fprintf( stderr, "Eliminatin' 0x%x - %d", Unitdeletequeue.back(), Unitdeletequeue.size() );
4875  fflush( stderr );
4876  VSFileSystem::vs_fprintf( stderr, "Eliminatin' %s\n", Unitdeletequeue.back()->name.c_str() );
4877 #endif
4878 #ifdef DESTRUCTDEBUG
4879  if ( Unitdeletequeue.back()->isSubUnit() )
4880  VSFileSystem::vs_fprintf( stderr, "Subunit Deleting (related to double dipping)" );
4881 #endif
4882  Unit *mydeleter = Unitdeletequeue.back();
4883  Unitdeletequeue.pop_back();
4884  delete mydeleter;
4885 
4886 #ifdef DESTRUCTDEBUG
4887  VSFileSystem::vs_fprintf( stderr, "Completed %d\n", Unitdeletequeue.size() );
4888  fflush( stderr );
4889 #endif
4890  }
4891 }
4892 
4893 Unit * makeBlankUpgrade( string templnam, int faction )
4894 {
4895  Unit *bl = UnitFactory::createServerSideUnit( templnam.c_str(), true, faction );
4896  for (int i = bl->numCargo()-1; i >= 0; i--) {
4897  int q = bl->GetCargo( i ).quantity;
4898  bl->RemoveCargo( i, q );
4899  }
4900  bl->Mass = 0;
4901  return bl;
4902 }
4903 
4904 static const string LOAD_FAILED = "LOAD_FAILED";
4905 
4906 const Unit * makeFinalBlankUpgrade( string name, int faction )
4907 {
4908  char *unitdir = GetUnitDir( name.c_str() );
4909  string limiternam = name;
4910  if (unitdir != name)
4911  limiternam = string( unitdir )+string( ".blank" );
4912  free( unitdir );
4913  const Unit *lim = UnitConstCache::getCachedConst( StringIntKey( limiternam, faction ) );
4914  if (!lim)
4915  lim = UnitConstCache::setCachedConst( StringIntKey( limiternam, faction ), makeBlankUpgrade( limiternam, faction ) );
4916  if (lim->name == LOAD_FAILED)
4917  lim = NULL;
4918  return lim;
4919 }
4920 
4921 const Unit * makeTemplateUpgrade( string name, int faction )
4922 {
4923  char *unitdir = GetUnitDir( name.c_str() );
4924  string limiternam = string( unitdir )+string( ".template" );
4925  free( unitdir );
4926  const Unit *lim = UnitConstCache::getCachedConst( StringIntKey( limiternam, faction ) );
4927  if (!lim) {
4928  lim =
4930  faction ), UnitFactory::createUnit( limiternam.c_str(), true, faction ) );
4931  }
4932  if (lim->name == LOAD_FAILED)
4933  lim = NULL;
4934  return lim;
4935 }
4936 
4937 const Unit * loadUnitByCache( std::string name, int faction )
4938 {
4939  const Unit *temprate = UnitConstCache::getCachedConst( StringIntKey( name, faction ) );
4940  if (!temprate)
4941  temprate =
4942  UnitConstCache::setCachedConst( StringIntKey( name, faction ), UnitFactory::createUnit( name.c_str(), true, faction ) );
4943  return temprate;
4944 }
4945 
4946 bool DestroySystem( float hull, float maxhull, float numhits )
4947 {
4948  static float damage_chance = XMLSupport::parse_float( vs_config->getVariable( "physics", "damage_chance", ".005" ) );
4949  static float guaranteed_chance = XMLSupport::parse_float( vs_config->getVariable( "physics", "definite_damage_chance", ".1" ) );
4950  float chance = 1-( damage_chance*(guaranteed_chance+(maxhull-hull)/maxhull) );
4951  if (numhits > 1)
4952  chance = pow( chance, numhits );
4953  return rand01() > chance;
4954 }
4955 
4956 bool DestroyPlayerSystem( float hull, float maxhull, float numhits )
4957 {
4958  static float damage_chance = XMLSupport::parse_float( vs_config->getVariable( "physics", "damage_player_chance", ".5" ) );
4959  static float guaranteed_chance = XMLSupport::parse_float( vs_config->getVariable( "physics", "definite_damage_chance", ".1" ) );
4960  float chance = 1-( damage_chance*(guaranteed_chance+(maxhull-hull)/maxhull) );
4961  if (numhits > 1)
4962  chance = pow( chance, numhits );
4963  bool ret = (rand01() > chance);
4964  if (ret) {
4965  //printf("DAAAAAAMAGED!!!!\n");
4966  }
4967  return ret;
4968 }
4969 
4970 const char *DamagedCategory = "upgrades/Damaged/";
4971 //short fix
4972 float Unit::DealDamageToHullReturnArmor( const Vector &pnt, float damage, float* &targ )
4973 {
4974  float percent;
4975 #ifndef ISUCK
4976  if (hull < 0)
4977  return -1;
4978 #endif
4979  if (pnt.i > 0) {
4980  if (pnt.j > 0) {
4981  if (pnt.k > 0)
4982  targ = &armor.frontlefttop;
4983  else
4984  targ = &armor.backlefttop;
4985  } else {
4986  if (pnt.k > 0)
4987  targ = &armor.frontleftbottom;
4988  else
4989  targ = &armor.backleftbottom;
4990  }
4991  } else {
4992  if (pnt.j > 0) {
4993  if (pnt.k > 0)
4994  targ = &armor.frontrighttop;
4995  else
4996  targ = &armor.backrighttop;
4997  } else {
4998  if (pnt.k > 0)
4999  targ = &armor.frontrightbottom;
5000  else
5001  targ = &armor.backrightbottom;
5002  }
5003  }
5004  //short fix
5005  float absdamage = damage >= 0 ? damage : -damage;
5006  float denom = (*targ+hull);
5007  percent = (denom > absdamage && denom != 0) ? absdamage/denom : (denom == 0 ? 0.0 : 1.0);
5008  //ONLY APLY DAMAGE ON SERVER SIDE
5009  if (Network == NULL || SERVER) {
5010  if (percent == -1)
5011  return -1;
5012  static float damage_factor_for_sound =
5013  XMLSupport::parse_float( vs_config->getVariable( "audio", "damage_factor_for_sound", ".001" ) );
5014  bool did_hull_damage = true;
5015  if (absdamage < *targ) {
5016  if ( (*targ)*damage_factor_for_sound <= absdamage )
5017  ArmorDamageSound( pnt );
5018  //short fix
5019  *targ -= apply_float_to_unsigned_int( absdamage );
5020  did_hull_damage = false;
5021  }
5022  static bool system_damage_on_armor =
5023  XMLSupport::parse_bool( vs_config->getVariable( "physics", "system_damage_on_armor", "false" ) );
5024  if (system_damage_on_armor || did_hull_damage) {
5025  if (did_hull_damage) {
5026  absdamage -= *targ;
5027  damage = damage >= 0 ? absdamage : -absdamage;
5028  *targ = 0;
5029  }
5030  if (numCargo() > 0) {
5031  if ( DestroySystem( hull, maxhull, numCargo() ) ) {
5032  int which = rand()%numCargo();
5033  static std::string Restricted_items = vs_config->getVariable( "physics", "indestructable_cargo_items", "" );
5034  //why not downgrade _add GetCargo(which).content.find("add_")!=0&&
5035  if (GetCargo( which ).GetCategory().find( "upgrades/" ) == 0
5036  && GetCargo( which ).GetCategory().find( DamagedCategory ) != 0
5037  && GetCargo( which ).GetContent().find( "mult_" ) != 0
5038  && Restricted_items.find( GetCargo( which ).GetContent() ) == string::npos) {
5039  int lenupgrades = strlen( "upgrades/" );
5040  GetCargo( which ).category = string( DamagedCategory )+GetCargo( which ).GetCategory().substr(
5041  lenupgrades );
5042  static bool NotActuallyDowngrade =
5043  XMLSupport::parse_bool( vs_config->getVariable( "physics", "separate_system_flakiness_component",
5044  "false" ) );
5045  if (!NotActuallyDowngrade) {
5046  const Unit *downgrade =
5047  loadUnitByCache( GetCargo( which ).content, FactionUtil::GetFactionIndex( "upgrades" ) );
5048  if (downgrade) {
5049  if ( 0 == downgrade->GetNumMounts() && downgrade->SubUnits.empty() ) {
5050  double percentage = 0;
5051  this->Downgrade( downgrade, 0, 0, percentage, NULL );
5052  }
5053  }
5054  }
5055  }
5056  }
5057  }
5058  bool isplayer = _Universe->isPlayerStarship( this );
5059  //hull > damage is similar to hull>absdamage|| damage<0
5060  if ( (!isplayer) || _Universe->AccessCockpit()->godliness <= 0 || hull > damage || system_damage_on_armor ) {
5061  static float system_failure =
5062  XMLSupport::parse_float( vs_config->getVariable( "physics", "indiscriminate_system_destruction", ".25" ) );
5063  if ( (!isplayer) && DestroySystem( hull, maxhull, 1 ) )
5064  DamageRandSys( system_failure*rand01()+(1-system_failure)*( 1-(hull > 0 ? absdamage/hull : 1.0f) ), pnt );
5065  else if ( isplayer && DestroyPlayerSystem( hull, maxhull, 1 ) )
5066  DamageRandSys( system_failure*rand01()+(1-system_failure)*( 1-(hull > 0 ? absdamage/hull : 1.0f) ), pnt );
5067  if (did_hull_damage) {
5068  if (damage > 0) {
5069  if (hull*damage_factor_for_sound <= damage)
5070  HullDamageSound( pnt );
5071  //FIXME
5072  hull -= damage;
5073  } else {
5074  //DISABLING WEAPON CODE HERE
5075  static float disabling_constant =
5076  XMLSupport::parse_float( vs_config->getVariable( "physics", "disabling_weapon_constant", "1" ) );
5077  if (hull > 0)
5078  pImage->LifeSupportFunctionality += disabling_constant*damage/hull;
5079  if (pImage->LifeSupportFunctionality < 0) {
5080  pImage->LifeSupportFunctionalityMax += pImage->LifeSupportFunctionality;
5081  pImage->LifeSupportFunctionality = 0;
5082  if (pImage->LifeSupportFunctionalityMax < 0)
5083  pImage->LifeSupportFunctionalityMax = 0;
5084  }
5085  }
5086  }
5087  } else {
5088  _Universe->AccessCockpit()->godliness -= absdamage;
5089  if ( DestroyPlayerSystem( hull, maxhull, 1 ) )
5090  //get system damage...but live!
5091  DamageRandSys( rand01()*.5+.2, pnt );
5092  }
5093  }
5094  if (hull < 0) {
5096  int upgradesfac = FactionUtil::GetUpgradeFaction();
5097 
5098  static float cargoejectpercent =
5099  XMLSupport::parse_float( vs_config->getVariable( "physics", "eject_cargo_percent", "1" ) );
5100 
5101  static float hulldamtoeject =
5102  XMLSupport::parse_float( vs_config->getVariable( "physics", "hull_damage_to_eject", "100" ) );
5103  if (hull > -hulldamtoeject) {
5104  static float autoejectpercent =
5105  XMLSupport::parse_float( vs_config->getVariable( "physics", "autoeject_percent", ".5" ) );
5106  if (SERVER || Network == NULL) {
5107  if (rand() < (RAND_MAX*autoejectpercent) && isUnit() == UNITPTR) {
5108  static bool player_autoeject =
5109  XMLSupport::parse_bool( vs_config->getVariable( "physics", "player_autoeject", "true" ) );
5110  if ( faction != neutralfac && faction != upgradesfac
5111  && ( player_autoeject || NULL == _Universe->isPlayerStarship( this ) ) )
5112  EjectCargo( (unsigned int) -1 );
5113  }
5114  }
5115  }
5116  static unsigned int max_dump_cargo =
5117  XMLSupport::parse_int( vs_config->getVariable( "physics", "max_dumped_cargo", "15" ) );
5118  unsigned int dumpedcargo = 0;
5119  if (SERVER || Network == NULL) {
5120  if (faction != neutralfac && faction != upgradesfac) {
5121  for (unsigned int i = 0; i < numCargo(); ++i)
5122  if (vsrandom.rand() < (VS_RAND_MAX*cargoejectpercent) && dumpedcargo++ < max_dump_cargo)
5123  EjectCargo( i );
5124  }
5125  }
5126 #ifdef ISUCK
5127  Destroy();
5128 #endif
5129  PrimeOrders();
5130  maxenergy = energy = 0;
5131  Split( rand()%3+1 );
5132 #ifndef ISUCK
5133  Destroy();
5134  return -1;
5135 #endif
5136  }
5137  }
5139  if ( !FINITE( percent ) )
5140  percent = 0;
5141  return percent;
5142 }
5143 
5144 bool withinShield( const ShieldFacing &facing, float theta, float rho )
5145 {
5146  float theta360 = theta+2*3.1415926536;
5147  return rho >= facing.rhomin && rho < facing.rhomax
5148  && ( (theta >= facing.thetamin
5149  && theta < facing.thetamax) || (theta360 >= facing.thetamin && theta360 < facing.thetamax) );
5150 }
5151 
5152 float Unit::DealDamageToShield( const Vector &pnt, float &damage )
5153 {
5154  float percent = 0;
5155  float *targ = NULL; //short fix
5156  float theta = atan2( pnt.i, pnt.k );
5157  float rho = atan( pnt.j/sqrt( pnt.k*pnt.k+pnt.i*pnt.i ) );
5158  //ONLY APPLY DAMAGES IN NON-NETWORKING OR ON SERVER SIDE
5159  for (int i = 0; i < shield.number; ++i)
5160  if ( withinShield( shield.range[i], theta, rho ) ) {
5161  if (shield.shield.max[i]) {
5162  //comparing with max
5163  float tmp = damage/shield.shield.max[i];
5164  if (tmp > percent) percent = tmp;
5165  }
5166  targ = &shield.shield.cur[i];
5167  if (Network == NULL || SERVER) {
5168  if (damage > *targ) {
5169  damage -= *targ;
5170  *targ = 0;
5171  } else {
5172  //short fix
5173  *targ -= damage;
5174  damage = 0;
5175  break;
5176  }
5177  }
5178  }
5179  if ( !FINITE( percent ) )
5180  percent = 0;
5181  return percent;
5182 }
5183 
5184 bool Unit::ShieldUp( const Vector &pnt ) const
5185 {
5186  const int shieldmin = 5;
5187  static float nebshields = XMLSupport::parse_float( vs_config->getVariable( "physics", "nebula_shield_recharge", ".5" ) );
5188  if (nebula != NULL || nebshields > 0)
5189  return false;
5190  switch (shield.number)
5191  {
5192  case 2:
5193  return ( (pnt.k > 0) ? (shield.shield2fb.front) : (shield.shield2fb.back) ) > shieldmin;
5194  case 8:
5195  if (pnt.i > 0) {
5196  if (pnt.j > 0) {
5197  if (pnt.k > 0)
5198  return shield.shield8.frontlefttop > shieldmin;
5199  else
5200  return shield.shield8.backlefttop > shieldmin;
5201  } else {
5202  if (pnt.k > 0)
5203  return shield.shield8.frontleftbottom > shieldmin;
5204  else
5205  return shield.shield8.backleftbottom > shieldmin;
5206  }
5207  } else {
5208  if (pnt.j > 0) {
5209  if (pnt.k > 0)
5210  return shield.shield8.frontrighttop > shieldmin;
5211  else
5212  return shield.shield8.backrighttop > shieldmin;
5213  } else {
5214  if (pnt.k > 0)
5215  return shield.shield8.frontrightbottom > shieldmin;
5216  else
5217  return shield.shield8.backrightbottom > shieldmin;
5218  }
5219  }
5220  break;
5221  case 4:
5222  if ( fabs( pnt.k ) > fabs( pnt.i ) ) {
5223  if (pnt.k > 0)
5224  return shield.shield4fbrl.front > shieldmin;
5225  else
5226  return shield.shield4fbrl.back > shieldmin;
5227  } else {
5228  if (pnt.i > 0)
5229  return shield.shield4fbrl.left > shieldmin;
5230  else
5231  return shield.shield4fbrl.right > shieldmin;
5232  }
5233  return false;
5234 
5235  default:
5236  return false;
5237  }
5238 }
5239 
5240 /*
5241  **********************************************************************************
5242  **** UNIT_WEAPON STUFF
5243  **********************************************************************************
5244  */
5245 
5247 {
5248  if ( !SubUnits.empty() ) {
5249  Unit *su;
5250  bool inrange = (targ != NULL) ? InRange( targ ) : true;
5251  if (inrange) {
5252  for (un_iter iter = getSubUnits(); (su = *iter); ++iter) {
5253  su->Target( targ );
5254  su->TargetTurret( targ );
5255  }
5256  }
5257  }
5258 }
5259 
5260 void WarpPursuit( Unit *un, StarSystem *sourcess, std::string destination )
5261 {
5262  static bool AINotUseJump = XMLSupport::parse_bool( vs_config->getVariable( "physics", "no_ai_jump_points", "false" ) );
5263  if (AINotUseJump) {
5264  static float seconds_per_parsec =
5265  XMLSupport::parse_float( vs_config->getVariable( "physics", "seconds_per_parsec", "10" ) );
5266  float ttime =
5267  ( SystemLocation( sourcess->getFileName() )-SystemLocation( destination ) ).Magnitude()*seconds_per_parsec;
5268  un->jump.delay += float_to_int( ttime );
5269  sourcess->JumpTo( un, NULL, destination, true, true );
5270  un->jump.delay -= float_to_int( ttime );
5271  }
5272 }
5273 
5274 //WARNING : WHEN TURRETS WE MAY NOT WANT TO ASK THE SERVER FOR INFOS ! ONLY FOR LOCAL PLAYERS (_Universe-isStarship())
5275 void Unit::LockTarget( bool myboo )
5276 {
5277  computer.radar.locked = myboo;
5278  if ( myboo && computer.radar.canlock == false && false == UnitUtil::isSignificant( Target() ) )
5279  computer.radar.locked = false;
5280 }
5281 
5282 void Unit::Target( Unit *targ )
5283 {
5284  if (targ == this)
5285  return;
5286  ObjSerial oldtarg = 0;
5287  if ( computer.target.GetUnit() )
5288  oldtarg = computer.target.GetUnit()->GetSerial();
5289  if ( !( activeStarSystem == NULL || activeStarSystem == _Universe->activeStarSystem() ) ) {
5290  if (SERVER && computer.target.GetUnit() != NULL)
5291  VSServer->BroadcastTarget( GetSerial(), oldtarg, 0, this->getStarSystem()->GetZone() );
5292  computer.target.SetUnit( NULL );
5293  return;
5294  }
5295  if (targ) {
5296  if (targ->activeStarSystem == _Universe->activeStarSystem() || targ->activeStarSystem == NULL) {
5297  if ( targ != Unit::Target() ) {
5298  for (int i = 0; i < GetNumMounts(); ++i)
5299  mounts[i].time_to_lock = mounts[i].type->LockTime;
5300  if (SERVER && computer.target.GetUnit() != targ)
5301  VSServer->BroadcastTarget( GetSerial(), oldtarg, targ->GetSerial(), this->getStarSystem()->GetZone() );
5302  computer.target.SetUnit( targ );
5303  LockTarget( false );
5304  }
5305  } else {
5306  if (jump.drive != -1) {
5307  bool found = false;
5308  Unit *u;
5309  for (un_kiter i = _Universe->activeStarSystem()->getUnitList().constIterator(); (u = *i) != NULL; ++i)
5310  if ( !u->GetDestinations().empty() ) {
5311  if ( std::find( u->GetDestinations().begin(), u->GetDestinations().end(),
5312  targ->activeStarSystem->getFileName() ) != u->GetDestinations().end() ) {
5313  Target( u );
5314  ActivateJumpDrive( 0 );
5315  found = true;
5316  }
5317  }
5318  if ( !found && !_Universe->isPlayerStarship( this ) )
5320  } else {
5321  if (SERVER && computer.target.GetUnit() != NULL)
5322  VSServer->BroadcastTarget( GetSerial(), oldtarg, 0, this->getStarSystem()->GetZone() );
5323  computer.target.SetUnit( NULL );
5324  }
5325  }
5326  } else {
5327  if (SERVER && computer.target.GetUnit() != NULL)
5328  VSServer->BroadcastTarget( GetSerial(), oldtarg, 0, this->getStarSystem()->GetZone() );
5329  computer.target.SetUnit( NULL );
5330  }
5331 }
5332 
5334 {
5335  computer.force_velocity_ref = !!targ;
5336  computer.velocity_ref.SetUnit( targ );
5337 }
5338 
5339 void Unit::SetOwner( Unit *target )
5340 {
5341  owner = target;
5342 }
5343 
5344 void Unit::Cloak( bool loak )
5345 {
5346  damages |= CLOAK_DAMAGED;
5347  if (loak) {
5348  static bool warp_energy_for_cloak =
5349  XMLSupport::parse_bool( vs_config->getVariable( "physics", "warp_energy_for_cloak", "true" ) );
5350  if ( pImage->cloakenergy < (warp_energy_for_cloak ? warpenergy : energy) ) {
5351  pImage->cloakrate = (pImage->cloakrate >= 0) ? pImage->cloakrate : -pImage->cloakrate;
5352  //short fix
5353  if (cloaking < -1 && pImage->cloakrate != 0) {
5354  //short fix
5355  cloaking = 2147483647;
5356  } else {}
5357  }
5358  } else {
5359  pImage->cloakrate = (pImage->cloakrate >= 0) ? -pImage->cloakrate : pImage->cloakrate;
5360  if (cloaking == cloakmin)
5361  ++cloaking;
5362  }
5363 }
5364 
5366 {
5367  for (int i = 0; i < GetNumMounts(); ++i)
5368  if (mounts[i].status < Mount::DESTROYED)
5369  if (mounts[i].type->size != weapon_info::SPECIAL)
5370  mounts[i].Activate( Missile );
5371 }
5372 
5374 {
5375  if ( ( isMissile( type ) ) == Missile )
5376  if (status == INACTIVE)
5377  status = ACTIVE;
5378 }
5379 
5382 {
5383  if ( ( isMissile( type ) ) == Missile )
5384  if (status == ACTIVE)
5385  status = INACTIVE;
5386 }
5387 
5389 {
5390  if (this->GetNumMounts() == 0) {
5391  Unit *tur = NULL;
5392  for (un_iter i = this->getSubUnits(); (tur = *i) != NULL; ++i)
5393  tur->UnFire();
5394  } else {
5395  int playernum = _Universe->whichPlayerStarship( this );
5396  vector< int >unFireRequests;
5397  for (int i = 0; i < GetNumMounts(); ++i) {
5398  if (mounts[i].status != Mount::ACTIVE)
5399  continue;
5400  if ( ( SERVER || (Network && playernum >= 0) ) && mounts[i].processed != Mount::UNFIRED )
5401  unFireRequests.push_back( i );
5402  mounts[i].UnFire(); //turns off beams;
5403  }
5404  if ( !unFireRequests.empty() ) {
5405  if (SERVER)
5406  VSServer->BroadcastUnfire( this->serial, unFireRequests, this->getStarSystem()->GetZone() );
5407  else
5408  Network[playernum].unfireRequest( this->serial, unFireRequests );
5409  }
5410  }
5411 }
5412 
5414 void Unit::ActivateGuns( const weapon_info *sz, bool ms )
5415 {
5416  for (int j = 0; j < 2; ++j)
5417  for (int i = 0; i < GetNumMounts(); ++i)
5418  if (mounts[i].type == sz) {
5419  if ( mounts[i].status < Mount::DESTROYED && mounts[i].ammo != 0 && (isMissile( mounts[i].type ) == ms) )
5420  mounts[i].Activate( ms );
5421  else
5422  sz = mounts[(i+1)%GetNumMounts()].type;
5423  }
5424 }
5425 
5426 typedef std::set< int >WeaponGroup;
5427 
5428 template < bool FORWARD >
5430 {
5431 public:
5432  bool operator()( const WeaponGroup &a, const WeaponGroup &b ) const
5433  {
5434  if ( a.size() == b.size() ) {
5435  for (WeaponGroup::const_iterator iterA = a.begin(), iterB = b.begin();
5436  iterA != a.end() && iterB != b.end();
5437  ++iterA, ++iterB) {
5438  if ( (*iterA) < (*iterB) )
5439  return FORWARD;
5440  else if ( (*iterB) < (*iterA) )
5441  return !FORWARD;
5442  }
5443  return false;
5444  } else if ( a.size() < b.size() ) {
5445  return FORWARD;
5446  } else {
5447  return !FORWARD;
5448  }
5449  }
5450 
5451  typedef std::set< WeaponGroup, WeaponComparator< FORWARD > >WeaponGroupSet;
5452 
5453  static bool checkmount( Unit *un, int i, bool missile )
5454  {
5455  return un->mounts[i].status < Mount::DESTROYED && ( ( isMissile( un->mounts[i].type ) ) == missile )
5456  && un->mounts[i].ammo != 0;
5457  }
5458 
5459  static bool isSpecial( const Mount &mount )
5460  {
5461  return mount.type->size == weapon_info::SPECIAL || mount.type->size == weapon_info::SPECIALMISSILE;
5462  }
5463 
5464  static bool notSpecial( const Mount &mount )
5465  {
5466  return !isSpecial( mount );
5467  }
5468 
5469  static void ToggleWeaponSet( Unit *un, bool missile )
5470  {
5471  if (un->mounts.size() == 0) {
5472  Unit *tur = NULL;
5473  for (un_iter i = un->getSubUnits(); (tur = *i) != NULL; ++i)
5474  ToggleWeaponSet( tur, missile );
5475  return;
5476  }
5477  WeaponGroup lightMissiles;
5478  WeaponGroup heavyMissiles;
5479  WeaponGroup allWeapons;
5480  WeaponGroup allWeaponsNoSpecial;
5481  WeaponGroupSet myset;
5482  unsigned int i;
5483  typename WeaponGroupSet::const_iterator iter;
5484  printf( "ToggleWeaponSet: %s\n", FORWARD ? "true" : "false" );
5485  for (i = 0; i < un->mounts.size(); ++i)
5486  if ( checkmount( un, i, missile ) ) {
5487  WeaponGroup mygroup;
5488  for (unsigned int j = 0; j < un->mounts.size(); ++j)
5489  if (un->mounts[j].type == un->mounts[i].type)
5490  if ( checkmount( un, j, missile ) )
5491  mygroup.insert( j );
5492  //WIll ignore if already there.
5493  myset.insert( mygroup );
5494  allWeapons.insert( i );
5495  if ( notSpecial( un->mounts[i] ) )
5496  allWeaponsNoSpecial.insert( i );
5497  }
5498  const WeaponGroupSet mypairset( myset );
5499  for (iter = mypairset.begin(); iter != mypairset.end(); ++iter)
5500  if ( (*iter).size() && notSpecial( un->mounts[( *( (*iter).begin() ) )] ) ) {
5501  typename WeaponGroupSet::const_iterator iter2;
5502  for (iter2 = mypairset.begin(); iter2 != mypairset.end(); ++iter2)
5503  if ( (*iter2).size() && notSpecial( un->mounts[( *( (*iter2).begin() ) )] ) ) {
5504  WeaponGroup myGroup;
5505  set_union( (*iter).begin(), (*iter).end(), (*iter2).begin(), (*iter2).end(),
5506  inserter( myGroup, myGroup.begin() ) );
5507  myset.insert( myGroup );
5508  }
5509  }
5510  static bool allow_special_with_weapons =
5511  XMLSupport::parse_bool( vs_config->getVariable( "physics", "special_and_normal_gun_combo", "true" ) );
5512  if (allow_special_with_weapons)
5513  myset.insert( allWeapons );
5514  myset.insert( allWeaponsNoSpecial );
5515  for (iter = myset.begin(); iter != myset.end(); ++iter) {
5516  for (WeaponGroup::const_iterator iter2 = (*iter).begin(); iter2 != (*iter).end(); ++iter2)
5517  printf( "%d:%s ", *iter2, un->mounts[*iter2].type->weapon_name.c_str() );
5518  printf( "\n" );
5519  }
5520  WeaponGroup activeWeapons;
5521  printf( "CURRENT: " );
5522  for (i = 0; i < un->mounts.size(); ++i)
5523  if ( un->mounts[i].status == Mount::ACTIVE && checkmount( un, i, missile ) ) {
5524  activeWeapons.insert( i );
5525  printf( "%d:%s ", i, un->mounts[i].type->weapon_name.c_str() );
5526  }
5527  printf( "\n" );
5528  iter = myset.upper_bound( activeWeapons );
5529  if ( iter == myset.end() )
5530  iter = myset.begin();
5531  if ( iter == myset.end() )
5532  return;
5533  for (i = 0; i < un->mounts.size(); ++i)
5534  un->mounts[i].DeActive( missile );
5535  printf( "ACTIVE: " );
5536  for (WeaponGroup::const_iterator iter2 = (*iter).begin(); iter2 != (*iter).end(); ++iter2) {
5537  printf( "%d:%s ", *iter2, un->mounts[*iter2].type->weapon_name.c_str() );
5538  un->mounts[*iter2].Activate( missile );
5539  }
5540  printf( "\n" );
5541  printf( "ToggleWeapon end...\n" );
5542  }
5543 };
5544 
5545 void Unit::ToggleWeapon( bool missile, bool forward )
5546 {
5547  if (forward)
5549  else
5551 }
5552 
5554 {
5555  owner = target;
5556  Unit *su;
5557  for (un_iter iter = getSubUnits(); (su = *iter); ++iter)
5558  su->SetRecursiveOwner( target );
5559 }
5560 
5562 {
5563  bool missilelock = false;
5564  bool dumblock = false;
5565  for (int i = 0; i < GetNumMounts(); ++i) {
5566  if ( mounts[i].status == Mount::ACTIVE && mounts[i].type->LockTime > 0 && mounts[i].time_to_lock <= 0
5567  && isMissile( mounts[i].type ) )
5568  missilelock = true;
5569  else if (mounts[i].status == Mount::ACTIVE && mounts[i].type->LockTime == 0 && isMissile( mounts[i].type )
5570  && mounts[i].time_to_lock <= 0)
5571  dumblock = true;
5572  }
5573  return missilelock ? 1 : (dumblock ? -1 : 0);
5574 }
5575 
5576 /*
5577  **********************************************************************************
5578  **** UNIT_COLLIDE STUFF
5579  **********************************************************************************
5580  */
5581 
5582 //virtual
5583 bool Unit::Explode( bool draw, float timeit )
5584 {
5585  return true;
5586 }
5587 
5589 {
5590  static float debrisTime = XMLSupport::parse_float( vs_config->getVariable( "physics", "debris_time", "500" ) );
5591  return std::min(pImage->timeexplode / debrisTime, 1.0f);
5592 }
5593 
5595 {
5596  if (!killed) {
5597  if (hull >= 0)
5598  hull = -1;
5599  for (int beamcount = 0; beamcount < GetNumMounts(); ++beamcount)
5600  DestroyMount( &mounts[beamcount] );
5601  //The server send a kill notification to all concerned clients but not if it is an upgrade
5602  if (SERVER && this->serial) {
5603  VSServer->sendKill( this->serial, this->getStarSystem()->GetZone() );
5604  this->serial = 0;
5605  }
5606  if ( !Explode( false, SIMULATION_ATOM ) )
5607  Kill();
5608  }
5609 }
5610 
5612 {
5613  assert( 0 ); //deprecated... many less collisions with subunits out of the table
5614 #if 0
5615  for (int i = 0; i < numsubunit; ++i) {
5616  subunits[i]->CollideInfo.object.u = name;
5617  subunits[i]->SetCollisionParent( name );
5618  }
5619 #endif
5620 }
5621 
5622 double Unit::getMinDis( const QVector &pnt )
5623 {
5624  float minsofar = 1e+10;
5625  float tmpvar;
5626  int i;
5627  Vector TargetPoint( cumulative_transformation_matrix.getP() );
5628 
5629 #ifdef VARIABLE_LENGTH_PQR
5630  //the scale factor of the current UNIT
5631  float SizeScaleFactor = sqrtf( TargetPoint.Dot( TargetPoint ) );
5632 #endif
5633  for (i = 0; i < nummesh(); ++i) {
5634  TargetPoint = (Transform( cumulative_transformation_matrix, meshdata[i]->Position() ).Cast()-pnt).Cast();
5635  tmpvar = sqrtf( TargetPoint.Dot( TargetPoint ) )-meshdata[i]->rSize()
5636 #ifdef VARIABLE_LENGTH_PQR
5637  *SizeScaleFactor
5638 #endif
5639  ;
5640  if (tmpvar < minsofar)
5641  minsofar = tmpvar;
5642  }
5643  Unit *su;
5644  for (un_kiter ui = viewSubUnits(); (su = *ui); ++ui) {
5645  tmpvar = su->getMinDis( pnt );
5646  if (tmpvar < minsofar)
5647  minsofar = tmpvar;
5648  }
5649  return minsofar;
5650 }
5651 
5652 //This function should not be used on server side
5653 extern vector< Vector >perplines;
5654 extern vector< int > turretcontrol;
5655 float Unit::querySphereClickList( const QVector &st, const QVector &dir, float err ) const
5656 {
5657  int i;
5658  float retval = 0;
5659  float adjretval = 0;
5660  const Matrix *tmpo = &cumulative_transformation_matrix;
5661 
5662  Vector TargetPoint( tmpo->getP() );
5663  for (i = 0; i < nummesh(); ++i) {
5664  TargetPoint = Transform( *tmpo, meshdata[i]->Position() );
5665  Vector origPoint = TargetPoint;
5666 
5667  perplines.push_back( TargetPoint );
5668  //find distance away from the line now :-)
5669  //find scale factor of end on start to get line.
5670  QVector tst = TargetPoint.Cast()-st;
5671  float k = tst.Dot( dir );
5672  TargetPoint = ( tst-k*(dir) ).Cast();
5673  perplines.push_back( origPoint-TargetPoint );
5674  if (TargetPoint.Dot( TargetPoint )
5675  < err*err
5676  +meshdata[i]->rSize()*meshdata[i]->rSize()+2*err*meshdata[i]->rSize()
5677  ) {
5678  if (retval == 0) {
5679  retval = k;
5680  adjretval = k;
5681  if (adjretval < 0) {
5682  adjretval += meshdata[i]->rSize();
5683  if (adjretval > 0)
5684  adjretval = .001;
5685  }
5686  } else {
5687  if ( retval > 0 && k < retval && k > -meshdata[i]->rSize() ) {
5688  retval = k;
5689  adjretval = k;
5690  if (adjretval < 0) {
5691  adjretval += meshdata[i]->rSize();
5692  if (adjretval > 0)
5693  adjretval = .001;
5694  }
5695  }
5696  if (retval < 0 && k+meshdata[i]->rSize() > retval) {
5697  retval = k;
5698  adjretval = k+meshdata[i]->rSize();
5699  if (adjretval > 0)
5700  //THRESHOLD;
5701  adjretval = .001;
5702  }
5703  }
5704  }
5705  }
5706  const Unit *su;
5707  for (un_kiter ui = viewSubUnits(); (su = *ui); ++ui) {
5708  float tmp = su->querySphereClickList( st, dir, err );
5709  if (tmp == 0)
5710  continue;
5711  if (retval == 0) {
5712  retval = tmp;
5713  } else {
5714  if (adjretval > 0 && tmp < adjretval) {
5715  retval = tmp;
5716  adjretval = tmp;
5717  }
5718  if (adjretval < 0 && tmp > adjretval) {
5719  retval = tmp;
5720  adjretval = tmp;
5721  }
5722  }
5723  }
5724  return adjretval;
5725 }
5726 
5727 /*
5728  **********************************************************************************
5729  **** UNIT_DOCK STUFF
5730  **********************************************************************************
5731  */
5732 
5733 const std::vector< struct DockingPorts >& Unit::DockingPortLocations() const
5734 {
5735  return pImage->dockingports;
5736 }
5737 
5739 {
5740  std::vector< Unit* >::iterator lookcleared;
5741  if ( ( lookcleared =
5742  std::find( targ->pImage->clearedunits.begin(), targ->pImage->clearedunits.end(),
5743  this ) ) != targ->pImage->clearedunits.end() ) {
5744  targ->pImage->clearedunits.erase( lookcleared );
5745  return true;
5746  } else {
5747  return false;
5748  }
5749 }
5750 
5751 bool Unit::RequestClearance( Unit *dockingunit )
5752 {
5753  if ( std::find( pImage->clearedunits.begin(), pImage->clearedunits.end(), dockingunit ) == pImage->clearedunits.end() )
5754  pImage->clearedunits.push_back( dockingunit );
5755  return true;
5756 }
5757 
5758 void Unit::FreeDockingPort( unsigned int i )
5759 {
5760  if (pImage->dockedunits.size() == 1)
5761  docked &= (~DOCKING_UNITS);
5762  unsigned int whichdock = pImage->dockedunits[i]->whichdock;
5763  pImage->dockingports[whichdock].Occupy(false);
5764  pImage->dockedunits[i]->uc.SetUnit( NULL );
5765  delete pImage->dockedunits[i];
5766  pImage->dockedunits.erase( pImage->dockedunits.begin()+i );
5767 }
5768 
5770  const Transformation &changeold,
5771  const Transformation &changenew )
5772 {
5773  Quaternion bak = holder.orientation;
5774  holder.position = holder.position-changeold.position;
5775 
5776  Quaternion invandrest = changeold.orientation.Conjugate();
5777  invandrest *= changenew.orientation;
5778  holder.orientation *= invandrest;
5779  Matrix m;
5780 
5781  invandrest.to_matrix( m );
5782  holder.position = TransformNormal( m, holder.position );
5783 
5784  holder.position = holder.position+changenew.position;
5785  static bool changeddockedorient =
5786  ( XMLSupport::parse_bool( vs_config->getVariable( "physics", "change_docking_orientation", "false" ) ) );
5787  if (!changeddockedorient)
5788  holder.orientation = bak;
5789  return holder;
5790 }
5791 
5792 extern void ExecuteDirector();
5793 extern void SwitchUnits( Unit*, Unit* );
5794 
5796 {
5797  for (unsigned int i = 0; i < pImage->dockedunits.size(); ++i) {
5798  Unit *un;
5799  if ( ( un = pImage->dockedunits[i]->uc.GetUnit() ) == NULL ) {
5800  FreeDockingPort( i );
5801  i--;
5802  continue;
5803  }
5805  un->curr_physical_state = HoldPositionWithRespectTo( un->curr_physical_state, prev_physical_state, curr_physical_state );
5806  un->NetForce = Vector( 0, 0, 0 );
5807  un->NetLocalForce = Vector( 0, 0, 0 );
5808  un->NetTorque = Vector( 0, 0, 0 );
5809  un->NetLocalTorque = Vector( 0, 0, 0 );
5810  un->AngularVelocity = Vector( 0, 0, 0 );
5811  un->Velocity = Vector( 0, 0, 0 );
5812  if ( un == _Universe->AccessCockpit()->GetParent() ) {
5814  for (unsigned int i = 0; i < pImage->clearedunits.size(); ++i)
5815  //this is a hack because we don't have an interface to say "I want to buy a ship" this does it if you press shift-c in the base
5816  if (pImage->clearedunits[i] == un) {
5817  pImage->clearedunits.erase( pImage->clearedunits.begin()+i );
5818  un->UpgradeInterface( this );
5819  }
5820  }
5821  //now we know the unit's still alive... what do we do to him *G*
5823  }
5824 }
5825 
5828 {
5829  static bool WCfuelhack = XMLSupport::parse_bool( vs_config->getVariable( "physics", "fuel_equals_warp", "false" ) );
5830  if (WCfuelhack)
5831  this->warpenergy = this->fuel;
5832  float tmp = this->maxwarpenergy;
5833  if (tmp < this->jump.energy)
5834  tmp = this->jump.energy;
5835  if (tmp > this->warpenergy) {
5836  this->warpenergy = tmp;
5837  if (WCfuelhack)
5838  this->fuel = this->warpenergy;
5839  return true;
5840  }
5841  return false;
5842 }
5843 
5844 void UpdateMasterPartList( Unit* );
5845 int Unit::ForceDock( Unit *utdw, unsigned int whichdockport )
5846 {
5847  if (utdw->pImage->dockingports.size() <= whichdockport)
5848  return 0;
5849  utdw->pImage->dockingports[whichdockport].Occupy(true);
5850 
5851  utdw->docked |= DOCKING_UNITS;
5852  utdw->pImage->dockedunits.push_back( new DockedUnits( this, whichdockport ) );
5853  //NETFIXME: Broken on server.
5854  if ( (!Network) && (!SERVER) && utdw->pImage->dockingports[whichdockport].IsInside() ) {
5855  RemoveFromSystem();
5856  SetVisible( false );
5857  docked |= DOCKED_INSIDE;
5858  } else {
5859  docked |= DOCKED;
5860  }
5861  pImage->DockedTo.SetUnit( utdw );
5862  computer.set_speed = 0;
5863  if ( this == _Universe->AccessCockpit()->GetParent() )
5864  this->RestoreGodliness();
5866  unsigned int cockpit = UnitUtil::isPlayerStarship( this );
5867 
5868  static float MinimumCapacityToRefuelOnLand =
5869  XMLSupport::parse_float( vs_config->getVariable( "physics", "MinimumWarpCapToRefuelDockeesAutomatically", "0" ) );
5870  float capdata = utdw->WarpCapData();
5871  if ( (capdata >= MinimumCapacityToRefuelOnLand) && ( this->RefillWarpEnergy() ) ) {
5872  if ( cockpit >= 0 && cockpit < _Universe->numPlayers() ) {
5873  static float docking_fee = XMLSupport::parse_float( vs_config->getVariable( "general", "fuel_docking_fee", "0" ) );
5874  _Universe->AccessCockpit( cockpit )->credits -= docking_fee;
5875  }
5876  }
5877  if ( (capdata < MinimumCapacityToRefuelOnLand) && (this->faction == utdw->faction) ) {
5878  if (utdw->WarpEnergyData() > this->WarpEnergyData() && utdw->WarpEnergyData() > this->jump.energy) {
5879  this->IncreaseWarpEnergy( false, this->jump.energy );
5880  utdw->DecreaseWarpEnergy( false, this->jump.energy );
5881  }
5882  if (utdw->WarpEnergyData() < this->WarpEnergyData() && this->WarpEnergyData() > utdw->jump.energy) {
5883  utdw->IncreaseWarpEnergy( false, utdw->jump.energy );
5884  this->DecreaseWarpEnergy( false, utdw->jump.energy );
5885  }
5886  }
5887  if ( cockpit >= 0 && cockpit < _Universe->numPlayers() ) {
5888  static float docking_fee = XMLSupport::parse_float( vs_config->getVariable( "general", "docking_fee", "0" ) );
5889  if (_Universe->AccessCockpit( cockpit )->credits >= docking_fee)
5890  _Universe->AccessCockpit( cockpit )->credits -= docking_fee;
5891  else if (_Universe->AccessCockpit( cockpit )->credits >= 0)
5892  _Universe->AccessCockpit( cockpit )->credits = 0;
5893  }
5894  std::set< Unit* >::iterator arrested = arrested_list_do_not_dereference.find( this );
5895  if ( arrested != arrested_list_do_not_dereference.end() ) {
5896  arrested_list_do_not_dereference.erase( arrested );
5897  //do this for jail time
5898  for (unsigned int j = 0; j < 100000; ++j)
5899  for (unsigned int i = 0; i < active_missions.size(); ++i)
5900  ExecuteDirector();
5901  }
5902  return whichdockport+1;
5903 }
5904 
5905 int Unit::Dock( Unit *utdw )
5906 {
5907  //Do only if non networking mode or if server (for both Network==NULL)
5908  if (Network == NULL) {
5909  if ( docked&(DOCKED_INSIDE|DOCKED) )
5910  return 0;
5911  std::vector< Unit* >::iterator lookcleared;
5912  if ( ( lookcleared = std::find( utdw->pImage->clearedunits.begin(),
5913  utdw->pImage->clearedunits.end(), this ) ) != utdw->pImage->clearedunits.end() ) {
5914  int whichdockport;
5915  if ( ( whichdockport = utdw->CanDockWithMe( this ) ) != -1 ) {
5916  utdw->pImage->clearedunits.erase( lookcleared );
5917  return ForceDock( utdw, whichdockport );
5918  }
5919  }
5920  return 0;
5921  } else {
5922  //Send a dock request
5923  int playernum = _Universe->whichPlayerStarship( this );
5924  if (playernum >= 0)
5925  Network[playernum].dockRequest( utdw->serial );
5926  }
5927  return 0;
5928 }
5929 
5930 inline bool insideDock( const DockingPorts &dock, const QVector &pos, float radius )
5931 {
5932  if (dock.IsOccupied())
5933  return false;
5934  return IsShorterThan(pos - dock.GetPosition(), double(radius + dock.GetRadius()));
5935 }
5936 
5937 int Unit::CanDockWithMe( Unit *un, bool force )
5938 {
5939  //don't need to check relation: already cleared.
5940 
5941  // If your unit has docking ports then we check if any of our docking
5942  // ports overlap with any of the station's docking ports.
5943  // Otherwise we simply check if our unit overlaps with any of the
5944  // station's docking ports.
5945  for (unsigned int i = 0; i < pImage->dockingports.size(); ++i)
5946  {
5947  if ( !un->pImage->dockingports.empty() )
5948  {
5949  for (unsigned int j = 0; j < un->pImage->dockingports.size(); ++j)
5950  {
5951  if ( insideDock( pImage->dockingports[i],
5952  InvTransform( GetTransformation(),
5954  un->pImage->dockingports[j].GetPosition().Cast() ) ),
5955  un->pImage->dockingports[j].GetRadius() ) )
5956  {
5957  // We cannot dock if we are already docked
5958  if ( ( ( un->docked&(DOCKED_INSIDE|DOCKED) ) == 0 ) && ( !(docked&DOCKED_INSIDE) ) )
5959  return i;
5960  }
5961  }
5962  }
5963  else if ( insideDock( pImage->dockingports[i],
5964  InvTransform( GetTransformation(), un->Position() ),
5965  un->rSize() ) )
5966  {
5967  return i;
5968  }
5969  }
5970  if (force) {
5971  for (unsigned int i = 0; i < pImage->dockingports.size(); ++i)
5972  if (!pImage->dockingports[i].IsOccupied())
5973  return i;
5974  }
5975  return -1;
5976 }
5977 
5978 bool Unit::IsCleared( const Unit *DockingUnit ) const
5979 {
5980  return std::find( pImage->clearedunits.begin(), pImage->clearedunits.end(), DockingUnit ) != pImage->clearedunits.end();
5981 }
5982 
5984 {
5985  return pImage && (pImage->clearedunits.size() > 0);
5986 }
5987 
5988 bool Unit::isDocked( const Unit *d ) const
5989 {
5990  if (!d)
5991  return false;
5992  if ( !( d->docked&(DOCKED_INSIDE|DOCKED) ) )
5993  return false;
5994  for (unsigned int i = 0; i < pImage->dockedunits.size(); ++i) {
5995  Unit *un;
5996  if ( ( un = pImage->dockedunits[i]->uc.GetUnit() ) != NULL )
5997  if (un == d)
5998  return true;
5999  }
6000  return false;
6001 }
6002 
6003 extern vector< int >switchunit;
6004 extern vector< int >turretcontrol;
6005 
6006 bool Unit::UnDock( Unit *utdw )
6007 {
6008  unsigned int i = 0;
6009  if (this->name == "return_to_cockpit") {
6010  if (this->faction == utdw->faction)
6011  this->owner = utdw;
6012  else
6013  this->owner = NULL;
6014  }
6015  VSFileSystem::vs_dprintf(3,"Asking to undock\n");
6016  if ( Network != NULL && !SERVER && !_Universe->netLocked() ) {
6017  cerr<<"Sending an undock notification"<<endl;
6018  int playernum = _Universe->whichPlayerStarship( this );
6019  if (playernum >= 0)
6020  Network[playernum].undockRequest( utdw->serial );
6021  }
6022  for (i = 0; i < utdw->pImage->dockedunits.size(); ++i)
6023  if (utdw->pImage->dockedunits[i]->uc.GetUnit() == this) {
6024  utdw->FreeDockingPort( i );
6025  i--;
6026  SetVisible( true );
6027  docked &= ( ~(DOCKED_INSIDE|DOCKED) );
6028  pImage->DockedTo.SetUnit( NULL );
6029  Velocity = utdw->Velocity;
6030  static float launch_speed = XMLSupport::parse_float( vs_config->getVariable( "physics", "launch_speed", "-1" ) );
6031  static bool auto_turn_towards =
6032  XMLSupport::parse_bool( vs_config->getVariable( "physics", "undock_turn_away", "true" ) );
6033  if (Network || SERVER) {
6034  auto_turn_towards = false;
6035  launch_speed = -1;
6036  }
6037  if (launch_speed > 0)
6038  computer.set_speed = launch_speed;
6039  if (auto_turn_towards) {
6040  for (int i = 0; i < 3; ++i) {
6041  Vector methem( RealPosition( this )-RealPosition( utdw ).Cast() );
6042  methem.Normalize();
6043  Vector p, q, r;
6044  GetOrientation( p, q, r );
6045  p = methem.Cross( r );
6046  float theta = p.Magnitude();
6047  if (theta*theta > .00001) {
6048  p *= (asin( theta )/theta);
6049  Rotate( p );
6050  GetOrientation( p, q, r );
6051  }
6052  if (r.Dot( methem ) < 0)
6053  Rotate( p*(PI/theta) );
6054  }
6055  }
6056  if (name == "return_to_cockpit" || this->name == "return_to_cockpit") {
6057  while ( turretcontrol.size() <= _Universe->CurrentCockpit() )
6058  turretcontrol.push_back( 0 );
6060  }
6061  // Send notification that a ship has undocked from a station
6062  _Universe->AccessCockpit()->OnDockEnd(utdw, this);
6063  return true;
6064  }
6065  return false;
6066 }
6067 
6068 /*
6069  **********************************************************************************
6070  **** UNIT_CUSTOMIZE STUFF
6071  **********************************************************************************
6072  */
6073 #define UPGRADEOK (1)
6074 #define NOTTHERE (0)
6075 #define CAUSESDOWNGRADE (-1)
6076 #define LIMITEDBYTEMPLATE (-2)
6077 
6078 const Unit * getUnitFromUpgradeName( const string &upgradeName, int myUnitFaction = 0 );
6079 
6080 typedef double (*adder)( double a, double b );
6081 typedef double (*percenter)( double a, double b, double c );
6082 typedef bool (*comparer)( double a, double b );
6083 
6084 bool GreaterZero( double a, double b )
6085 {
6086  return a >= 0;
6087 }
6088 
6089 double AddUp( double a, double b )
6090 {
6091  return a+b;
6092 }
6093 
6094 double MultUp( double a, double b )
6095 {
6096  return a*b;
6097 }
6098 
6099 double GetsB( double a, double b )
6100 {
6101  return b;
6102 }
6103 
6104 bool AGreaterB( double a, double b )
6105 {
6106  return a > b;
6107 }
6108 
6109 double SubtractUp( double a, double b )
6110 {
6111  return a-b;
6112 }
6113 
6114 double SubtractClamp( double a, double b )
6115 {
6116  return (a-b < 0) ? 0 : a-b;
6117 }
6118 
6119 bool ALessB( double a, double b )
6120 {
6121  return a < b;
6122 }
6123 
6124 double computePercent( double old, double upgrade, double newb )
6125 {
6126  if (newb)
6127  return old/newb;
6128  else
6129  return 0;
6130 }
6131 
6132 double computeWorsePercent( double old, double upgrade, double isnew )
6133 {
6134  if (old)
6135  return isnew/old;
6136  else
6137  return 1;
6138 }
6139 
6140 double computeAdderPercent( double a, double b, double c )
6141 {
6142  return 0;
6143 }
6144 double computeMultPercent( double a, double b, double c )
6145 {
6146  return 0;
6147 }
6148 double computeDowngradePercent( double old, double upgrade, double isnew )
6149 {
6150  if (upgrade)
6151  return (old-isnew)/upgrade;
6152  else
6153  return 0;
6154 }
6155 
6156 static int UpgradeFloat( double &result, double tobeupgraded, double upgrador, double templatelimit, double (*myadd)( double,
6157  double ),
6158  bool (*betterthan)( double a,
6159  double b ), double nothing, double completeminimum, double (*computepercentage)(
6160  double oldvar,
6161  double upgrador,
6162  double newvar ), double &percentage, bool forcedowngrade, bool usetemplate, double at_least_this,
6163  bool (*atLeastthiscompare)(
6164  double a,
6165  double b ) = AGreaterB, bool clamp = false, bool force_nothing = false )
6166 {
6167  //if upgrador is better than nothing
6168  if (upgrador != nothing || force_nothing) {
6169  if (clamp)
6170  if (tobeupgraded > upgrador)
6171  upgrador = tobeupgraded;
6172  float newsum = (*myadd)(tobeupgraded, upgrador);
6173  //if we're downgrading
6174  if (!force_nothing && newsum < tobeupgraded && at_least_this >= upgrador && at_least_this > newsum && at_least_this
6175  >= tobeupgraded)
6176  return newsum == upgrador ? CAUSESDOWNGRADE : NOTTHERE;
6177  if ( newsum != tobeupgraded && ( ( (*betterthan)(newsum, tobeupgraded) || forcedowngrade ) ) ) {
6178  if ( ( (*betterthan)(newsum, templatelimit) && usetemplate ) || newsum < completeminimum ) {
6179  if (!forcedowngrade)
6180  return LIMITEDBYTEMPLATE;
6181  if (newsum < completeminimum)
6182  newsum = completeminimum;
6183  else
6184  newsum = templatelimit;
6185  }
6187  percentage = (*computepercentage)(tobeupgraded, upgrador, newsum);
6188  if ( (*atLeastthiscompare)(at_least_this, newsum) && (!force_nothing) ) {
6189  if ( (*atLeastthiscompare)(at_least_this, tobeupgraded) )
6190  //no shift
6191  newsum = tobeupgraded;
6192  else
6193  //set it to its min
6194  newsum = at_least_this;
6195  }
6196  result = newsum;
6197  return UPGRADEOK;
6198  } else {
6199  return CAUSESDOWNGRADE;
6200  }
6201  } else {
6202  return NOTTHERE;
6203  }
6204 }
6205 
6206 int UpgradeBoolval( int a, int upga, bool touchme, bool downgrade, int &numave, double &percentage, bool force_nothing )
6207 {
6208  if (downgrade) {
6209  if (a && upga) {
6210  if (touchme) (a = false);
6211  ++numave;
6212  ++percentage;
6213  }
6214  } else {
6215  if (!a && upga) {
6216  if (touchme) a = true;
6217  ++numave;
6218  ++percentage;
6219  } else if (force_nothing && a && !upga) {
6220  if (touchme) a = false;
6221  ++numave;
6222  ++percentage;
6223  }
6224  }
6225  return a;
6226 }
6227 
6229 {
6230  for (int i = 0; input_buffer[i] != '\0'; ++i)
6231  if (input_buffer[i] == '\n' || input_buffer[i] == '\r')
6232  input_buffer[i] = '\0';
6233 }
6234 
6235 bool Quit( const char *input_buffer )
6236 {
6237  if (strcasecmp( input_buffer, "exit" ) == 0
6238  || strcasecmp( input_buffer, "quit" ) == 0)
6239  return true;
6240  return false;
6241 }
6242 
6243 using std::string;
6244 
6245 void Tokenize( const string &str, vector< string > &tokens, const string &delimiters = " " )
6246 {
6247  //Skip delimiters at beginning.
6248  string::size_type lastPos = str.find_first_not_of( delimiters, 0 );
6249  //Find first "non-delimiter".
6250  string::size_type pos = str.find_first_of( delimiters, lastPos );
6251  while (string::npos != pos || string::npos != lastPos) {
6252  //Found a token, add it to the vector.
6253  tokens.push_back( str.substr( lastPos, pos-lastPos ) );
6254  //Skip delimiters. Note the "not_of"
6255  lastPos = str.find_first_not_of( delimiters, pos );
6256  //Find next "non-delimiter"
6257  pos = str.find_first_of( delimiters, lastPos );
6258  }
6259 }
6260 
6261 std::string CheckBasicSizes( const std::string tokens )
6262 {
6263  if (tokens.find( "small" ) != string::npos)
6264  return "small";
6265  if (tokens.find( "medium" ) != string::npos)
6266  return "medium";
6267  if (tokens.find( "large" ) != string::npos)
6268  return "large";
6269  if (tokens.find( "cargo" ) != string::npos)
6270  return "cargo";
6271  if (tokens.find( "LR" ) != string::npos || tokens.find( "massive" ) != string::npos)
6272  return "massive";
6273  return "";
6274 }
6275 
6276 class VCString : public std::string
6277 {
6278 public: VCString() {}
6279  VCString( const string &s ) : string( s ) {}
6280 };
6281 std::map< VCString, VCString >parseTurretSizes()
6282 {
6283  using namespace VSFileSystem;
6284  std::map< VCString, VCString >t;
6285  VSFile f;
6286  VSError err = f.OpenReadOnly( "units/subunits/size.txt", UnknownFile );
6287  if (err <= Ok) {
6288  int siz = f.Size();
6289  char *filedata = (char*) malloc( siz+1 );
6290  filedata[siz] = 0;
6291  while (f.ReadLine( filedata, siz ) == Ok) {
6292  std::string x( filedata );
6293  string::size_type len = x.find( "," );
6294  if (len != std::string::npos) {
6295  std::string y = x.substr( len+1 );
6296  x = x.substr( 0, len );
6297  len = y.find( "," );
6298  y = y.substr( 0, len );
6299  sscanf( y.c_str(), "%s", filedata );
6300  y = filedata;
6301  VCString key( x );
6302  VCString value( y );
6303  t[key] = value;
6304  }
6305  }
6306  free( filedata );
6307  f.Close();
6308  }
6309  return t;
6310 }
6311 
6312 std::string getTurretSize( const std::string &size )
6313 {
6314  static std::map< VCString, VCString > turretmap = parseTurretSizes();
6315  std::map< VCString, VCString >::iterator h = turretmap.find( size );
6316  if ( h != turretmap.end() )
6317  return (*h).second;
6318  vector< string >tokens;
6319  Tokenize( size, tokens, "_" );
6320  for (unsigned int i = 0; i < tokens.size(); ++i) {
6321  if (tokens[i].find( "turret" ) != string::npos) {
6322  string temp = CheckBasicSizes( tokens[i] );
6323  if ( !temp.empty() )
6324  return temp;
6325  } else {
6326  return tokens[i];
6327  }
6328  }
6329  return "capital";
6330 }
6331 
6333  int mountoffset,
6334  bool touchme,
6335  bool downgrade,
6336  int &numave,
6337  const Unit *templ,
6338  double &percentage )
6339 {
6340  int j;
6341  int i;
6342  bool cancompletefully = true;
6343  for (i = 0, j = mountoffset; i < up->GetNumMounts() && i < GetNumMounts() /*i should be GetNumMounts(), s'ok*/; ++i, ++j)
6344  //only mess with this if the upgrador has active mounts
6345  if (up->mounts[i].status == Mount::ACTIVE || up->mounts[i].status == Mount::INACTIVE) {
6346  //make sure since we're offsetting the starting we don't overrun the mounts
6347  bool isammo = ( string::npos != string( up->name ).find( "_ammo" ) ); //is this ammo for a weapon rather than an actual weapon
6348  bool ismissiletype =
6349  ( 0
6350  != ( up->mounts[i].type->size
6354 
6355  int jmod = j%GetNumMounts();
6356  if (!downgrade) {
6357  //if we wish to add guns instead of remove
6358  if (up->mounts[i].type->weapon_name.find( "_UPGRADE" ) == string::npos) {
6359  //check for capability increase rather than actual weapon upgrade
6360  //only look at this mount if it can fit in the rack
6361  if ( (unsigned int) (up->mounts[i].type->size) == (up->mounts[i].type->size&mounts[jmod].size) ) {
6362  if (up->mounts[i].type->weapon_name != mounts[jmod].type->weapon_name || mounts[jmod].status
6363  == Mount::DESTROYED || mounts[jmod].status == Mount::UNCHOSEN) {
6364  //If missile, can upgrade directly, if other type of ammo, needs actual gun to be present.
6365  if (isammo && !ismissiletype) {
6366  cancompletefully = false;
6367  } else {
6368  ++numave; //ok now we can compute percentage of used parts
6369  Mount upmount( up->mounts[i] );
6370  if (templ) {
6371  if (templ->GetNumMounts() > jmod) {
6372  if (templ->mounts[jmod].volume != -1)
6373  if (upmount.ammo*upmount.type->volume > templ->mounts[jmod].volume)
6374  upmount.ammo = (int) ( (templ->mounts[jmod].volume+1)/upmount.type->volume );
6375  }
6376  }
6377  //compute here
6378  percentage += mounts[jmod].Percentage( &upmount );
6379  //if we wish to modify the mounts
6380  if (touchme)
6381  //switch this mount with the upgrador mount
6382  mounts[jmod].ReplaceMounts( this, &upmount );
6383  }
6384  } else {
6385  if (isammo && up->mounts[i].type->weapon_name == mounts[jmod].type->weapon_name) {
6386  //if is ammo and is same weapon type
6387  int tmpammo = mounts[jmod].ammo;
6388  if (mounts[jmod].ammo != -1 && up->mounts[i].ammo != -1) {
6389  tmpammo += up->mounts[i].ammo;
6390  if (templ) {
6391  if (templ->GetNumMounts() > jmod) {
6392  if (templ->mounts[jmod].volume != -1) {
6393  if (templ->mounts[jmod].volume < mounts[jmod].type->volume*tmpammo) {
6394  tmpammo =
6395  (int) floor( .125
6396  +( (0
6397  +templ->mounts[jmod].volume)
6398  /mounts[jmod].type->volume ) );
6399  }
6400  }
6401  }
6402  }
6403  if (tmpammo*mounts[jmod].type->volume > mounts[jmod].volume)
6404  tmpammo = (int) floor( .125+( (0+mounts[jmod].volume)/mounts[jmod].type->volume ) );
6405  if (tmpammo > mounts[jmod].ammo) {
6406  cancompletefully = true;
6407  if (touchme)
6408  mounts[jmod].ammo = tmpammo;
6409  } else {
6410  cancompletefully = false;
6411  }
6412  }
6413  } else {
6414  cancompletefully = false;
6415  }
6416  }
6417  } else {
6418  //since we cannot fit the mount in the slot we cannot complete fully
6419  cancompletefully = false;
6420  }
6421  } else {
6422  unsigned int siz = 0;
6423  siz = ~siz;
6424  if (templ)
6425  if (templ->GetNumMounts() > jmod)
6426  siz = templ->mounts[jmod].size;
6427  if ( ( (siz&up->mounts[i].size)|mounts[jmod].size ) != mounts[jmod].size ) {
6428  if (touchme)
6429  mounts[jmod].size |= up->mounts[i].size;
6430  ++numave;
6431  ++percentage;
6432  } else {
6433  cancompletefully = false;
6434  }
6435  //we need to |= the mount type
6436  }
6437  } //DOWNGRADE
6438  else {
6439  if (up->mounts[i].type->weapon_name != "MOUNT_UPGRADE") {
6440  bool found = false; //we haven't found a matching gun to remove
6442  for (unsigned int k = 0; k < (unsigned int) GetNumMounts(); ++k) {
6443  //we want to start with bias
6444  int jkmod = (jmod+k)%GetNumMounts();
6445  if (Mount::UNCHOSEN == mounts[jkmod].status)
6446  //can't sell weapon that's already been sold/removed
6447  continue;
6449  if (strcasecmp( mounts[jkmod].type->weapon_name.c_str(),
6450  up->mounts[i].type->weapon_name.c_str() ) == 0) {
6451  //we got one, but check if we're trying to sell non-existent ammo
6452  if (isammo && mounts[jkmod].ammo <= 0)
6453  //whether it's gun ammo or a missile, you can't remove ammo from an infinite source, and you can't remove ammo if there isn't any
6454  continue;
6455  else
6456  found = true;
6458  percentage += mounts[jkmod].Percentage( &up->mounts[i] );
6459  //if we modify
6460  if (touchme) {
6461  //if downgrading ammo based upgrade, checks for infinite ammo
6462  if (isammo && up->mounts[i].ammo && up->mounts[i].ammo != -1 && mounts[jkmod].ammo != -1) {
6463  //remove upgrade-worth, else remove remaining
6464  mounts[jkmod].ammo -=
6465  (mounts[jkmod].ammo >= up->mounts[i].ammo) ? up->mounts[i].ammo : mounts[jkmod].ammo;
6466  //if none left
6467  if (!mounts[jkmod].ammo) {
6469  if (ismissiletype)
6470  mounts[jkmod].status = Mount::UNCHOSEN;
6471  }
6472  } else {
6474  mounts[jkmod].status = Mount::UNCHOSEN;
6475  mounts[jkmod].ammo = -1; //remove all ammo
6476  }
6477  }
6478  break;
6479  }
6480  }
6481  if (!found)
6482  //we did not find a matching weapon to remove
6483  cancompletefully = false;
6484  } else {
6485  bool found = false;
6486  static bool downmount =
6487  XMLSupport::parse_bool( vs_config->getVariable( "physics", "can_downgrade_mount_upgrades", "false" ) );
6488  if (downmount) {
6490  for (unsigned int k = 0; k < (unsigned int) GetNumMounts(); ++k) {
6491  //we want to start with bias
6492  int jkmod = (jmod+k)%GetNumMounts();
6493  if ( (up->mounts[i].size&mounts[jkmod].size) == (up->mounts[i].size) ) {
6494  if (touchme)
6495  mounts[jkmod].size &= (~up->mounts[i].size);
6496  ++percentage;
6497  ++numave;
6498  found = true;
6499  }
6500  }
6501  }
6502  if (!found)
6503  cancompletefully = false;
6504  }
6505  }
6506  }
6507  if ( i < up->GetNumMounts() )
6508  cancompletefully = false; //if we didn't reach the last mount that we wished to upgrade, we did not fully complete
6509 
6510  return cancompletefully;
6511 }
6512 
6513 Unit * CreateGenericTurret( std::string tur, int faction )
6514 {
6515  return new Unit( tur.c_str(), true, faction, "", 0, 0 );
6516 }
6517 
6518 bool Unit::UpgradeSubUnits( const Unit *up, int subunitoffset, bool touchme, bool downgrade, int &numave, double &percentage )
6519 {
6520  return UpgradeSubUnitsWithFactory( up, subunitoffset, touchme, downgrade, numave, percentage, &CreateGenericTurret );
6521 }
6522 
6523 bool Unit::UpgradeSubUnitsWithFactory( const Unit *up, int subunitoffset, bool touchme, bool downgrade, int &numave,
6524  double &percentage, Unit* (*createupgradesubunit)(std::string s,
6525  int
6526  faction) )
6527 {
6528  bool cancompletefully = true;
6529  int j;
6530  std::string turSize;
6531  un_iter ui;
6532  bool found = false;
6533  for (j = 0, ui = getSubUnits(); (*ui) != NULL && j < subunitoffset; ++ui, ++j) {}
6534  un_kiter upturrets;
6535  Unit *giveAway;
6536 
6537  giveAway = *ui;
6538  if (giveAway == NULL)
6539  return true;
6540  bool hasAnyTurrets = false;
6541  turSize = getTurretSize( giveAway->name );
6542  //begin goign through other unit's turrets
6543  for (upturrets = up->viewSubUnits(); ( (*upturrets) != NULL ) && ( (*ui) != NULL ); ++ui, ++upturrets) {
6544  hasAnyTurrets = true;
6545  const Unit *addtome;
6546 
6547  addtome = *upturrets; //set pointers
6548 
6549  bool foundthis = false;
6550  //if the new turret has any size at all
6551  if ( turSize == getTurretSize( addtome->name ) && addtome->rSize() && ( turSize+"_blank" != addtome->name.get() ) ) {
6552  if (!downgrade || addtome->name == giveAway->name) {
6553  found = true;
6554  foundthis = true;
6555  ++numave; //add it
6556  //add up percentage equal to ratio of sizes
6557  percentage += ( giveAway->rSize()/addtome->rSize() );
6558  }
6559  }
6560  if (foundthis) {
6561  if (touchme) {
6562  //if we wish to modify,
6563  Transformation addToMeCur = giveAway->curr_physical_state;
6564  Transformation addToMePrev = giveAway->prev_physical_state;
6565  giveAway->Kill(); //risky??
6566  ui.remove(); //remove the turret from the first unit
6567  //if we are upgrading swap them
6568  if (!downgrade) {
6569  Unit *addToMeNew = (*createupgradesubunit)(addtome->name, addtome->faction);
6570  addToMeNew->curr_physical_state = addToMeCur;
6571  addToMeNew->SetFaction( faction );
6572  addToMeNew->prev_physical_state = addToMePrev;
6573  //add unit to your ship
6574  ui.preinsert( addToMeNew );
6575  //set recursive owner
6576  addToMeNew->SetRecursiveOwner( this );
6577  } else {
6578  Unit *un; //make garbage unit
6579  //NOT 100% SURE A GENERIC UNIT CAN FIT (WAS GAME UNIT CREATION)
6580  //give a default do-nothing unit
6581  ui.preinsert( un = UnitFactory::createUnit( "upgrading_dummy_unit", true, faction ) );
6582  //WHAT?!?!?!?! 102302 ui.preinsert (un=new Unit(0));//give a default do-nothing unit
6583  un->SetFaction( faction );
6584  un->curr_physical_state = addToMeCur;
6585  un->prev_physical_state = addToMePrev;
6586  un->limits.yaw = 0;
6587  un->limits.pitch = 0;
6588  un->limits.roll = 0;
6589  un->limits.lateral = un->limits.retro = un->limits.forward = un->limits.afterburn = 0.0;
6590 
6591  un->name = turSize+"_blank";
6592  if (un->pImage->unitwriter != NULL)
6593  un->pImage->unitwriter->setName( un->name );
6594  un->SetRecursiveOwner( this );
6595  }
6596  }
6597  }
6598  }
6599  if (!found)
6600  return !hasAnyTurrets;
6601  if ( (*upturrets) != NULL )
6602  return false;
6603  return cancompletefully;
6604 }
6605 
6606 static void GCCBugCheckFloat( float *f, int offset )
6607 {
6608  if (f[offset] > 1)
6609  f[offset] = 1; //keep it real
6610 }
6611 
6612 bool Unit::canUpgrade( const Unit *upgrador,
6613  int mountoffset,
6614  int subunitoffset,
6615  int additive,
6616  bool force,
6617  double &percentage,
6618  const Unit *templ,
6619  bool force_change_on_nothing,
6620  bool gen_downgrade_list )
6621 {
6622  return UpAndDownGrade( upgrador,
6623  templ,
6624  mountoffset,
6625  subunitoffset,
6626  false,
6627  false,
6628  additive,
6629  force,
6630  percentage,
6631  this,
6632  force_change_on_nothing,
6633  gen_downgrade_list );
6634 }
6635 
6636 bool Unit::Upgrade( const Unit *upgrador,
6637  int mountoffset,
6638  int subunitoffset,
6639  int additive,
6640  bool force,
6641  double &percentage,
6642  const Unit *templ,
6643  bool force_change_on_nothing,
6644  bool gen_downgrade_list )
6645 {
6646  return UpAndDownGrade( upgrador,
6647  templ,
6648  mountoffset,
6649  subunitoffset,
6650  true,
6651  false,
6652  additive,
6653  force,
6654  percentage,
6655  this,
6656  force_change_on_nothing,
6657  gen_downgrade_list );
6658 }
6659 
6660 bool Unit::canDowngrade( const Unit *downgradeor,
6661  int mountoffset,
6662  int subunitoffset,
6663  double &percentage,
6664  const Unit *downgradelimit,
6665  bool gen_downgrade_list )
6666 {
6667  return UpAndDownGrade( downgradeor,
6668  NULL,
6669  mountoffset,
6670  subunitoffset,
6671  false,
6672  true,
6673  false,
6674  true,
6675  percentage,
6676  downgradelimit,
6677  false,
6678  gen_downgrade_list );
6679 }
6680 
6681 bool Unit::Downgrade( const Unit *downgradeor,
6682  int mountoffset,
6683  int subunitoffset,
6684  double &percentage,
6685  const Unit *downgradelimit,
6686  bool gen_downgrade_list )
6687 {
6688  return UpAndDownGrade( downgradeor,
6689  NULL,
6690  mountoffset,
6691  subunitoffset,
6692  true,
6693  true,
6694  false,
6695  true,
6696  percentage,
6697  downgradelimit,
6698  false,
6699  gen_downgrade_list );
6700 }
6701 
6703 {
6704  static float MyPercentMin =
6705  XMLSupport::parse_float( vs_config->getVariable( "general", "remove_downgrades_less_than_percent", ".9" ) );
6706  return MyPercentMin;
6707 }
6708 
6710 {
6711 public:
6712  string s;
6713  double d;
6714  DoubleName( string ss, double dd )
6715  {
6716  d = dd;
6717  s = ss;
6718  }
6720  {
6721  d = -FLT_MAX;
6722  }
6723 };
6724 
6725 extern int GetModeFromName( const char* );
6726 
6727 extern Unit * CreateGameTurret( std::string tur, int faction );
6728 
6729 extern char * GetUnitDir( const char* );
6730 double Unit::Upgrade( const std::string &file, int mountoffset, int subunitoffset, bool force, bool loop_through_mounts )
6731 {
6732 #if 0
6733  if (shield.number == 2)
6734  printf( "shields before %s %f %f", file.c_str(), shield.fb[2], shield.fb[3] );
6735  else
6736  printf( "shields before %s %d %d", file.c_str(), shield.fbrl.frontmax, shield.fbrl.backmax );
6737 #endif
6739  const Unit *up = UnitConstCache::getCachedConst( StringIntKey( file, upgradefac ) );
6740  if (!up)
6741  up = UnitConstCache::setCachedConst( StringIntKey( file, upgradefac ),
6742  UnitFactory::createUnit( file.c_str(), true, upgradefac ) );
6743  unsigned int cargonum;
6744  Cargo *cargo = GetCargo(file, cargonum);
6745  if (cargo)
6746  cargo->installed = true;
6747  char *unitdir = GetUnitDir( this->name.get().c_str() );
6748  string templnam = string( unitdir )+".template";
6749  const Unit *templ = UnitConstCache::getCachedConst( StringIntKey( templnam, this->faction ) );
6750  if (templ == NULL) {
6751  templ =
6753  this->faction ),
6754  UnitFactory::createUnit( templnam.c_str(), true, this->faction ) );
6755  }
6756  free( unitdir );
6757  double percentage = 0;
6758  if (up->name != "LOAD_FAILED") {
6759  for (int i = 0; percentage == 0; ++i) {
6760  if ( !this->Unit::Upgrade( up, mountoffset+i, subunitoffset+i,
6761  GetModeFromName( file.c_str() ), force, percentage,
6762  ( (templ->name == "LOAD_FAILED") ? NULL : templ ),
6763  false, false ) )
6764  percentage = 0;
6765  if (!loop_through_mounts || ( i+1 >= this->GetNumMounts() ) || percentage > 0)
6766  break;
6767  }
6768  }
6769 #if 0
6770  if (shield.number == 2)
6771  printf( "shields before %s %f %f", file.c_str(), shield.fb[2], shield.fb[3] );
6772  else
6773  printf( "shields before %s %d %d", file.c_str(), shield.fbrl.frontmax, shield.fbrl.backmax );
6774 #endif
6775 
6776  return percentage;
6777 }
6778 
6779 vsUMap< int, DoubleName >downgrademap;
6780 int curdowngrademapoffset = 5*sizeof (Unit);
6781 bool AddToDowngradeMap( std::string name, double value, int unitoffset, vsUMap< int, DoubleName > &tempdowngrademap )
6782 {
6783  using vsUMap;
6784  vsUMap< int, DoubleName >::iterator i = downgrademap.find( unitoffset );
6785  if ( i != downgrademap.end() ) {
6786  if ( (*i).second.d <= value ) {
6787  tempdowngrademap[unitoffset] = DoubleName( name, value );
6788  return true;
6789  }
6790  } else {
6791  tempdowngrademap[unitoffset] = DoubleName( name, value );
6792  return true;
6793  }
6794  return false;
6795 }
6796 
6798 {
6799  downgrademap.clear();
6800 }
6801 
6802 std::set< std::string >GetListOfDowngrades()
6803 {
6804  using vsUMap;
6805  vsUMap< int, DoubleName >::iterator i = downgrademap.begin();
6806  std::set< std::string >retval;
6807  for (; i != downgrademap.end(); ++i)
6808  retval.insert( (*i).second.s );
6809  return retval;
6810 }
6811 
6812 typedef vsUMap< const char*, bool > UnitHasRecursiveData;
6813 typedef vsUMap< std::string, UnitHasRecursiveData >FactionHasRecursiveData;
6814 typedef std::vector< FactionHasRecursiveData > HasRecursiveData;
6815 
6816 static HasRecursiveData has_recursive_data;
6817 static std::string upgradeString( "Upgrades" );
6818 
6819 static bool cell_has_recursive_data( const string &name, unsigned int fac, const char *key )
6820 {
6821  if ( fac < has_recursive_data.size() ) {
6822  FactionHasRecursiveData::const_iterator iter = has_recursive_data[fac].find( name );
6823  if ( iter != has_recursive_data[fac].end() ) {
6824  UnitHasRecursiveData::const_iterator iter2 = iter->second.find( key );
6825  if ( iter2 != iter->second.end() )
6826  return iter2->second;
6827  }
6828  } else {
6829  has_recursive_data.resize( fac+1 );
6830  }
6831  bool retval = false;
6832  string faction = FactionUtil::GetFactionName( fac );
6833  string lkey = key;
6834  string::size_type lkstart = 0;
6835  string::size_type lkend = lkey.find( '|' );
6836  //Big short circuit - avoids recursion
6837  while ( !retval && (lkstart != string::npos) ) {
6838  string skey = lkey.substr( lkstart, (lkend == string::npos) ? string::npos : lkend-lkstart );
6839  string lus = UniverseUtil::LookupUnitStat( name, faction, skey );
6840 
6841  retval = (lus.length() != 0);
6842 
6843  lkstart = (lkend != string::npos) ? lkend+1 : string::npos;
6844  lkend = lkey.find( '|', lkstart );
6845  }
6846  if (!retval) {
6847  //Big short circuit - avoids recursion
6848  string::size_type when;
6849  string upgrades = UniverseUtil::LookupUnitStat( name, faction, upgradeString );
6850  string::size_type ofs = 0;
6851  while ( !retval && ( ( when = upgrades.find( '{', ofs ) ) != string::npos ) ) {
6852  string::size_type where = upgrades.find( '}', when+1 );
6853  string upgrade = upgrades.substr( when+1, ( (where != string::npos) ? where-when-1 : string::npos ) );
6854  retval = cell_has_recursive_data( upgrade, fac, key );
6855  ofs = where+1;
6856  }
6857  }
6858  has_recursive_data[fac][name][key] = retval;
6859  return retval;
6860 }
6861 
6862 #define STDUPGRADE_SPECIFY_DEFAULTS( my, oth, temp, noth, dgradelimer, dgradelimerdefault, clamp, value_to_lookat ) \
6863  do { \
6864  retval = \
6865  ( \
6866  UpgradeFloat( \
6867  resultdoub, \
6868  my, \
6869  oth, \
6870  (templ != NULL) ? temp : 0, \
6871  Adder, Comparer, noth, noth, \
6872  Percenter, temppercent, \
6873  forcetransaction, \
6874  templ != NULL, \
6875  (downgradelimit != NULL) ? dgradelimer : dgradelimerdefault, \
6876  AGreaterB, \
6877  clamp, \
6878  force_change_on_nothing \
6879  ) \
6880  ); \
6881  if (retval == UPGRADEOK) \
6882  { \
6883  if (touchme) \
6884  my = resultdoub; \
6885  percentage += temppercent; \
6886  ++numave; \
6887  can_be_redeemed = true; \
6888  if (gen_downgrade_list) \
6889  AddToDowngradeMap( up->name, oth, ( (char*) &value_to_lookat )-(char*) this, tempdownmap ); \
6890  } \
6891  else if (retval != NOTTHERE) \
6892  { \
6893  if (retval == CAUSESDOWNGRADE) \
6894  needs_redemption = true; \
6895  else \
6896  cancompletefully = false; \
6897  } \
6898  } \
6899  while (0)
6900 
6901 #define STDUPGRADE( my, oth, temp, noth ) \
6902  do {STDUPGRADE_SPECIFY_DEFAULTS( my, \
6903  oth, \
6904  temp, \
6905  noth, \
6906  downgradelimit->my, \
6907  blankship->my, \
6908  false, \
6909  this->my ); } \
6910  while (0)
6911 
6912 #define STDUPGRADECLAMP( my, oth, temp, noth ) \
6913  do {STDUPGRADE_SPECIFY_DEFAULTS( my, \
6914  oth, \
6915  temp, \
6916  noth, \
6917  downgradelimit->my, \
6918  blankship->my, \
6919  !force_change_on_nothing, \
6920  this->my ); } \
6921  while (0)
6922 
6923 bool Unit::UpAndDownGrade( const Unit *up,
6924  const Unit *templ,
6925  int mountoffset,
6926  int subunitoffset,
6927  bool touchme,
6928  bool downgrade,
6929  int additive,
6930  bool forcetransaction,
6931  double &percentage,
6932  const Unit *downgradelimit,
6933  bool force_change_on_nothing,
6934  bool gen_downgrade_list )
6935 {
6936  percentage = 0;
6937  if (Network && !_Universe->netLocked() && touchme) {
6938  int playernum = _Universe->whichPlayerStarship( this );
6939  if (playernum >= 0) {
6940  ObjSerial buySerial = downgrade ? 0 : serial,
6941  sellSerial = downgrade ? serial : 0;
6942  Network[playernum].cargoRequest( buySerial, sellSerial,
6943  up->name, 0, mountoffset, subunitoffset );
6944  }
6945  return false;
6946  }
6947  if ( SERVER && touchme && !_Universe->netLocked() && getStarSystem() ) {
6948  //Server may not go here if it wants to send an atomic upgrade message.
6949  ObjSerial buySerial = downgrade ? 0 : serial,
6950  sellSerial = downgrade ? serial : 0;
6951  VSServer->BroadcastCargoUpgrade( serial, buySerial, sellSerial,
6952  up->name, 0, 0, 0, false, 0,
6953  mountoffset, subunitoffset, getStarSystem()->GetZone() );
6954  }
6955  static bool csv_cell_null_check = XMLSupport::parse_bool( vs_config->getVariable( "data", "empty_cell_check", "true" ) );
6956  int numave = 0;
6957  bool cancompletefully = true;
6958  bool can_be_redeemed = false;
6959  bool needs_redemption = false;
6960  if (mountoffset >= 0)
6961  cancompletefully = UpgradeMounts( up, mountoffset, touchme, downgrade, numave, templ, percentage );
6962  bool cancompletefully1 = true;
6963  if (subunitoffset >= 0)
6964  cancompletefully1 = UpgradeSubUnits( up, subunitoffset, touchme, downgrade, numave, percentage );
6965  cancompletefully = cancompletefully && cancompletefully1;
6966  adder Adder;
6967  comparer Comparer;
6968  percenter Percenter;
6969  vsUMap< int, DoubleName >tempdownmap;
6970  if (cancompletefully && cancompletefully1 && downgrade)
6971  if (percentage > 0)
6972  AddToDowngradeMap( up->name, 1, curdowngrademapoffset++, tempdownmap );
6973  float tmax_speed = up->computer.max_combat_speed;
6974  float tmax_ab_speed = up->computer.max_combat_ab_speed;
6975  float tmax_yaw_right = up->computer.max_yaw_right;
6976  float tmax_yaw_left = up->computer.max_yaw_left;
6977  float tmax_pitch_up = up->computer.max_pitch_up;
6978  float tmax_pitch_down = up->computer.max_pitch_down;
6979  float tmax_roll_right = up->computer.max_roll_right;
6980  float tmax_roll_left = up->computer.max_roll_left;
6981  float tlimits_yaw = up->limits.yaw;
6982  float tlimits_roll = up->limits.roll;
6983  float tlimits_pitch = up->limits.pitch;
6984  float tlimits_lateral = up->limits.lateral;
6985  float tlimits_vertical = up->limits.vertical;
6986  float tlimits_forward = up->limits.forward;
6987  float tlimits_retro = up->limits.retro;
6988  float tlimits_afterburn = up->limits.afterburn;
6989  if (downgrade) {
6990  Adder = &SubtractUp;
6991  Percenter = &computeDowngradePercent;
6992  Comparer = &GreaterZero;
6993  } else {
6994  if (additive == 1) {
6995  Adder = &AddUp;
6996  Percenter = &computeAdderPercent;
6997  } else if (additive == 2) {
6998  Adder = &MultUp;
6999  Percenter = &computeMultPercent;
7000  tmax_speed = XMLSupport::parse_float( speedStarHandler( XMLType( &tmax_speed ), NULL ) );
7001  tmax_ab_speed = XMLSupport::parse_float( speedStarHandler( XMLType( &tmax_ab_speed ), NULL ) );
7002  tmax_yaw_right = XMLSupport::parse_float( angleStarHandler( XMLType( &tmax_yaw_right ), NULL ) );
7003  tmax_yaw_left = XMLSupport::parse_float( angleStarHandler( XMLType( &tmax_yaw_left ), NULL ) );
7004  tmax_pitch_up = XMLSupport::parse_float( angleStarHandler( XMLType( &tmax_pitch_up ), NULL ) );
7005  tmax_pitch_down = XMLSupport::parse_float( angleStarHandler( XMLType( &tmax_pitch_down ), NULL ) );
7006  tmax_roll_right = XMLSupport::parse_float( angleStarHandler( XMLType( &tmax_roll_right ), NULL ) );
7007  tmax_roll_left = XMLSupport::parse_float( angleStarHandler( XMLType( &tmax_roll_left ), NULL ) );
7008  tlimits_yaw = XMLSupport::parse_float( angleStarHandler( XMLType( &tlimits_yaw ), NULL ) );
7009  tlimits_pitch = XMLSupport::parse_float( angleStarHandler( XMLType( &tlimits_pitch ), NULL ) );
7010  tlimits_roll = XMLSupport::parse_float( angleStarHandler( XMLType( &tlimits_roll ), NULL ) );
7011  tlimits_forward = XMLSupport::parse_float( accelStarHandler( XMLType( &tlimits_forward ), NULL ) );
7012  tlimits_retro = XMLSupport::parse_float( accelStarHandler( XMLType( &tlimits_retro ), NULL ) );
7013  tlimits_lateral = XMLSupport::parse_float( accelStarHandler( XMLType( &tlimits_lateral ), NULL ) );
7014  tlimits_vertical = XMLSupport::parse_float( accelStarHandler( XMLType( &tlimits_vertical ), NULL ) );
7015  tlimits_afterburn = XMLSupport::parse_float( accelStarHandler( XMLType( &tlimits_afterburn ), NULL ) );
7016  } else {
7017  Adder = &GetsB;
7018  Percenter = &computePercent;
7019  }
7020  Comparer = AGreaterB;
7021  }
7022  double resultdoub;
7023  int retval = 0; //"= 0" added by chuck_starchaser to shut off a warning about its possibly being used uninitialized
7024  double temppercent;
7025  static Unit *blankship = NULL;
7026  static bool initblankship = false;
7027  if (!initblankship) {
7028  blankship = this;
7029  initblankship = true;
7030  blankship = UnitFactory::createServerSideUnit( "upgrading_dummy_unit", true, FactionUtil::GetUpgradeFaction() );
7031  }
7032  //set up vars for "LookupUnitStat" to check for empty cells
7033  string upgrade_name = up->name;
7034  //Check SPEC stuff
7035  if ( !csv_cell_null_check || force_change_on_nothing
7036  || cell_has_recursive_data( upgrade_name, up->faction,
7037  "Spec_Interdiction|Warp_Min_Multiplier|Warp_Max_Multiplier" ) ) {
7038  if ( !csv_cell_null_check || force_change_on_nothing
7039  || cell_has_recursive_data( upgrade_name, up->faction, "Spec_Interdiction" ) ) {
7040  bool upneg = up->specInterdiction < 0;
7041  bool interdictionUnits = specInterdiction > 0;
7042  specInterdiction = fabs( specInterdiction );
7043  STDUPGRADE( specInterdiction, fabs( up->specInterdiction ), upneg ? fabs(
7044  templ->specInterdiction ) : templ->specInterdiction, 0 );
7045  if (upneg)
7046  specInterdiction = -specInterdiction;
7047  if ( interdictionUnits != (specInterdiction > 0) ) {
7048  StarSystem *ss = activeStarSystem;
7050  if (ss) {
7051  Unit *un;
7052  for (un_iter i = ss->gravitationalUnits().createIterator(); (un = *i); ++i)
7053  if (un == this) {
7054  i.remove();
7055  //NOTE: I think we can only be in here once
7056  break;
7057  }
7058  if (!interdictionUnits)
7059  //will interdict
7060  ss->gravitationalUnits().prepend( this );
7061  }
7062  }
7063  }
7064  if ( !csv_cell_null_check || force_change_on_nothing
7065  || cell_has_recursive_data( upgrade_name, up->faction, "Warp_Min_Multiplier" ) ) {
7066  STDUPGRADE( graphicOptions.MinWarpMultiplier,
7069  1 );
7070  }
7071  if ( !csv_cell_null_check || force_change_on_nothing
7072  || cell_has_recursive_data( upgrade_name, up->faction, "Warp_Max_Multiplier" ) ) {
7073  STDUPGRADE( graphicOptions.MaxWarpMultiplier,
7076  1 );
7077  }
7078  }
7079  //Check jump and jump/SPEC stuff
7080  if ( !csv_cell_null_check || force_change_on_nothing
7081  || cell_has_recursive_data( upgrade_name, up->faction,
7082  "Warp_Capacitor|Warp_Usage_Cost" ) ) {
7083  if ( !csv_cell_null_check || force_change_on_nothing
7084  || cell_has_recursive_data( upgrade_name, up->faction, "Warp_Capacitor" ) )
7085  STDUPGRADE( maxwarpenergy, up->maxwarpenergy, templ->maxwarpenergy, 0 );
7086  if ( !csv_cell_null_check || force_change_on_nothing
7087  || cell_has_recursive_data( upgrade_name, up->faction, "Warp_Usage_Cost" ) )
7088  STDUPGRADE( jump.insysenergy, up->jump.insysenergy, templ->jump.insysenergy, 0 );
7089 
7090 // for when we'll need more than one jump drive upgrade (Elite Strike?)
7091  if ( !csv_cell_null_check || force_change_on_nothing
7092  || cell_has_recursive_data( upgrade_name, up->faction, "Outsystem_Jump_Cost" ) )
7093  STDUPGRADE( jump.energy, up->jump.energy, templ->jump.energy, 0 );
7094  if ( !csv_cell_null_check || force_change_on_nothing
7095  || cell_has_recursive_data( upgrade_name, up->faction, "Jump_Drive_Delay" ) )
7096  STDUPGRADE( jump.delay, up->jump.delay, templ->jump.delay, 0 );
7097 
7098  }
7099 
7100 
7101  if ( !csv_cell_null_check || force_change_on_nothing
7102  || cell_has_recursive_data( upgrade_name, up->faction, "Armor_Front_Top_Right" ) ) {
7103  STDUPGRADE( armor.frontrighttop, up->armor.frontrighttop, templ->armor.frontrighttop, 0 );
7104  STDUPGRADE( armor.backrighttop, up->armor.backrighttop, templ->armor.backrighttop, 0 );
7105  STDUPGRADE( armor.frontlefttop, up->armor.frontlefttop, templ->armor.frontlefttop, 0 );
7106  STDUPGRADE( armor.backlefttop, up->armor.backlefttop, templ->armor.backlefttop, 0 );
7107  STDUPGRADE( armor.frontrightbottom, up->armor.frontrightbottom, templ->armor.frontrightbottom, 0 );
7108  STDUPGRADE( armor.backrightbottom, up->armor.backrightbottom, templ->armor.backrightbottom, 0 );
7109  STDUPGRADE( armor.frontleftbottom, up->armor.frontleftbottom, templ->armor.frontleftbottom, 0 );
7110  STDUPGRADE( armor.backleftbottom, up->armor.backleftbottom, templ->armor.backleftbottom, 0 );
7111  }
7112  float tmp = shield.recharge;
7113  if ( !csv_cell_null_check || force_change_on_nothing
7114  || cell_has_recursive_data( upgrade_name, up->faction, "Shield_Recharge" ) )
7115  STDUPGRADE( shield.recharge, up->shield.recharge, templ->shield.recharge, 0 );
7116  bool upgradedrecharge = (tmp != shield.recharge);
7117  if ( !csv_cell_null_check || force_change_on_nothing || cell_has_recursive_data( upgrade_name, up->faction, "Hull" ) )
7118  STDUPGRADE( hull, up->hull, templ->hull, 0 );
7119  if ( (maxhull < hull) && (hull != 0) )
7120  maxhull = hull;
7121  if ( !csv_cell_null_check || force_change_on_nothing
7122  || cell_has_recursive_data( upgrade_name, up->faction, "Reactor_Recharge" ) )
7123  STDUPGRADE( recharge, up->recharge, templ->recharge, 0 );
7124  static bool unittable = XMLSupport::parse_bool( vs_config->getVariable( "physics", "UnitTable", "false" ) );
7125  //Uncommon fields (capacities... rates... etc...)
7126  if ( !csv_cell_null_check || force_change_on_nothing
7127  || cell_has_recursive_data( upgrade_name, up->faction,
7128  "Heat_Sink_Rating|Repair_Droid|Hold_Volume|Upgrade_Storage_Volume|Equipment_Space|Hidden_Hold_Volume|ECM_Rating|Primary_Capacitor|Warp_Capacitor" ) )
7129  {
7130  if ( !csv_cell_null_check || force_change_on_nothing
7131  || cell_has_recursive_data( upgrade_name, up->faction, "Heat_Sink_Rating" ) )
7132  STDUPGRADE( HeatSink, up->HeatSink, templ->HeatSink, 0 );
7133  if ( !csv_cell_null_check || force_change_on_nothing
7134  || cell_has_recursive_data( upgrade_name, up->faction, "Repair_Droid" ) )
7135  STDUPGRADE( pImage->repair_droid, up->pImage->repair_droid, templ->pImage->repair_droid, 0 );
7136  if ( !csv_cell_null_check || force_change_on_nothing
7137  || cell_has_recursive_data( upgrade_name, up->faction, "Hold_Volume" ) )
7138  STDUPGRADE( pImage->CargoVolume, up->pImage->CargoVolume, templ->pImage->CargoVolume, 0 );
7139  if ( !csv_cell_null_check || force_change_on_nothing
7140  || cell_has_recursive_data( upgrade_name, up->faction, "Upgrade_Storage_Volume" ) )
7141  STDUPGRADE( pImage->UpgradeVolume, up->pImage->UpgradeVolume, templ->pImage->UpgradeVolume, 0 );
7142  if ( !csv_cell_null_check || force_change_on_nothing
7143  || cell_has_recursive_data( upgrade_name, up->faction, "Equipment_Space" ) )
7144  STDUPGRADE( pImage->equipment_volume, up->pImage->equipment_volume, templ->pImage->equipment_volume, 0 );
7145  if ( !csv_cell_null_check || force_change_on_nothing
7146  || cell_has_recursive_data( upgrade_name, up->faction, "Hidden_Hold_Volume" ) )
7147  STDUPGRADE( pImage->HiddenCargoVolume, up->pImage->HiddenCargoVolume, templ->pImage->HiddenCargoVolume, 0 );
7148  if ( !csv_cell_null_check || force_change_on_nothing
7149  || cell_has_recursive_data( upgrade_name, up->faction, "ECM_Rating" ) )
7150  STDUPGRADE( pImage->ecm, up->pImage->ecm, templ->pImage->ecm, 0 ); //ecm is unsigned --chuck_starchaser
7151  if ( !csv_cell_null_check || force_change_on_nothing
7152  || cell_has_recursive_data( upgrade_name, up->faction, "Primary_Capacitor" ) )
7153  STDUPGRADE( maxenergy, up->maxenergy, templ->maxenergy, 0 );
7154  }
7155  //Maneuvering stuff
7156  if ( !csv_cell_null_check || force_change_on_nothing
7157  || cell_has_recursive_data( upgrade_name, up->faction,
7158  "Maneuver_Yaw|Maneuver_Pitch|Maneuver_Roll|Left_Accel|Top_Accel|Retro_Accel|Forward_Accel|Afterburner_Accel|Default_Speed_Governor|Afterburner_Speed_Governor|Yaw_Governor|Pitch_Governor|Roll_Speed_Governor" ) )
7159  {
7160  if ( !csv_cell_null_check || force_change_on_nothing
7161  || cell_has_recursive_data( upgrade_name, up->faction, "Maneuver_Yaw" ) )
7162  STDUPGRADE( limits.yaw, tlimits_yaw, templ->limits.yaw, 0 );
7163  if ( !csv_cell_null_check || force_change_on_nothing
7164  || cell_has_recursive_data( upgrade_name, up->faction, "Maneuver_Pitch" ) )
7165  STDUPGRADE( limits.pitch, tlimits_pitch, templ->limits.pitch, 0 );
7166  if ( !csv_cell_null_check || force_change_on_nothing
7167  || cell_has_recursive_data( upgrade_name, up->faction, "Maneuver_Roll" ) )
7168  STDUPGRADE( limits.roll, tlimits_roll, templ->limits.roll, 0 );
7169  if ( !csv_cell_null_check || force_change_on_nothing
7170  || cell_has_recursive_data( upgrade_name, up->faction, "Left_Accel" ) )
7171  STDUPGRADE( limits.lateral, tlimits_lateral, templ->limits.lateral, 0 );
7172  if ( !csv_cell_null_check || force_change_on_nothing || cell_has_recursive_data( upgrade_name, up->faction, "Top_Accel" ) )
7173  STDUPGRADE( limits.vertical, tlimits_vertical, templ->limits.vertical, 0 );
7174  if ( !csv_cell_null_check || force_change_on_nothing
7175  || cell_has_recursive_data( upgrade_name, up->faction, "Retro_Accel" ) )
7176  STDUPGRADE( limits.retro, tlimits_retro, templ->limits.retro, 0 );
7177  if ( !csv_cell_null_check || force_change_on_nothing
7178  || cell_has_recursive_data( upgrade_name, up->faction, "Forward_Accel" ) )
7179  STDUPGRADE( limits.forward, tlimits_forward, templ->limits.forward, 0 );
7180  if ( !csv_cell_null_check || force_change_on_nothing
7181  || cell_has_recursive_data( upgrade_name, up->faction, "Afterburner_Accel" ) )
7182  STDUPGRADE( limits.afterburn, tlimits_afterburn, templ->limits.afterburn, 0 );
7183  if ( !csv_cell_null_check || force_change_on_nothing
7184  || cell_has_recursive_data( upgrade_name, up->faction, "Fuel_Capacity" ) )
7185  STDUPGRADE( fuel, up->fuel, templ->fuel, 0 );
7186  if ( !csv_cell_null_check || force_change_on_nothing
7187  || cell_has_recursive_data( upgrade_name, up->faction, "Default_Speed_Governor" ) )
7188  STDUPGRADE( computer.max_combat_speed, tmax_speed, templ->computer.max_combat_speed, 0 );
7189  if ( !csv_cell_null_check || force_change_on_nothing
7190  || cell_has_recursive_data( upgrade_name, up->faction, "Afterburner_Speed_Governor" ) )
7191  STDUPGRADE( computer.max_combat_ab_speed, tmax_ab_speed, templ->computer.max_combat_ab_speed, 0 );
7192  if ( !csv_cell_null_check || force_change_on_nothing
7193  || cell_has_recursive_data( upgrade_name, up->faction, "Yaw_Governor" ) ) {
7194  STDUPGRADE( computer.max_yaw_right, tmax_yaw_right, templ->computer.max_yaw_right, 0 );
7195  STDUPGRADE( computer.max_yaw_left, tmax_yaw_left, templ->computer.max_yaw_left, 0 );
7196  }
7197  if ( !csv_cell_null_check || force_change_on_nothing
7198  || cell_has_recursive_data( upgrade_name, up->faction, "Pitch_Governor" ) ) {
7199  STDUPGRADE( computer.max_pitch_down, tmax_pitch_down, templ->computer.max_pitch_down, 0 );
7200  STDUPGRADE( computer.max_pitch_up, tmax_pitch_up, templ->computer.max_pitch_up, 0 );
7201  }
7202  if ( !csv_cell_null_check || force_change_on_nothing
7203  || cell_has_recursive_data( upgrade_name, up->faction, "Roll_Speed_Governor" ) ) {
7204  STDUPGRADE( computer.max_roll_left, tmax_roll_left, templ->computer.max_roll_left, 0 );
7205  STDUPGRADE( computer.max_roll_right, tmax_roll_right, templ->computer.max_roll_right, 0 );
7206  }
7207  }
7208  //FIXME - do cell lookup later here
7209  static bool UpgradeCockpitDamage =
7210  XMLSupport::parse_bool( vs_config->getVariable( "physics", "upgrade_cockpit_damage", "false" ) );
7211  if (UpgradeCockpitDamage) {
7212  STDUPGRADE( pImage->fireControlFunctionality, up->pImage->fireControlFunctionality,
7214  (unittable ? 0 : 1) );
7215  STDUPGRADE( pImage->fireControlFunctionalityMax, up->pImage->fireControlFunctionalityMax,
7217  (unittable ? 0 : 1) );
7218  STDUPGRADE( pImage->SPECDriveFunctionality, up->pImage->SPECDriveFunctionality, templ->pImage->SPECDriveFunctionality,
7219  (unittable ? 0 : 1) );
7220  STDUPGRADE( pImage->SPECDriveFunctionalityMax, up->pImage->SPECDriveFunctionalityMax,
7222  (unittable ? 0 : 1) );
7223  STDUPGRADE( pImage->CommFunctionality, up->pImage->CommFunctionality, templ->pImage->CommFunctionality,
7224  (unittable ? 0 : 1) );
7225  STDUPGRADE( pImage->CommFunctionalityMax, up->pImage->CommFunctionalityMax, templ->pImage->CommFunctionalityMax,
7226  (unittable ? 0 : 1) );
7227  STDUPGRADE( pImage->LifeSupportFunctionality, up->pImage->LifeSupportFunctionality,
7229  (unittable ? 0 : 1) );
7230  STDUPGRADE( pImage->LifeSupportFunctionalityMax, up->pImage->LifeSupportFunctionalityMax,
7232  (unittable ? 0 : 1) );
7233  unsigned int upgrmax = (UnitImages< void >::NUMGAUGES+1+MAXVDUS)*2;
7234  for (unsigned int upgr = 0; upgr < upgrmax; upgr++)
7235  STDUPGRADE( pImage->cockpit_damage[upgr], up->pImage->cockpit_damage[upgr], templ->pImage->cockpit_damage[upgr],
7236  (unittable ? 0 : 1) );
7237  for (unsigned int upgr = 0; upgr < upgrmax; ++upgr)
7238  GCCBugCheckFloat( pImage->cockpit_damage, upgr );
7239  }
7240  bool upgradedshield = false;
7241  if ( !csv_cell_null_check || force_change_on_nothing
7242  || cell_has_recursive_data( upgrade_name, up->faction, "Shield_Front_Top_Right" ) ) {
7243  if (shield.number == up->shield.number) {
7244  float a, b, c, d;
7245  float aa, bb, cc, dd;
7246  switch (shield.number)
7247  {
7248  case 2:
7249  a = shield.shield2fb.frontmax;
7250  b = shield.shield2fb.backmax;
7251  STDUPGRADE( shield.shield2fb.frontmax, up->shield.shield2fb.frontmax, templ->shield.shield2fb.frontmax, 0 );
7252  STDUPGRADE( shield.shield2fb.backmax, up->shield.shield2fb.backmax, templ->shield.shield2fb.backmax, 0 );
7253  if (shield.shield2fb.frontmax != a) shield.shield2fb.front = shield.shield2fb.frontmax;
7254  if (shield.shield2fb.backmax != b) shield.shield2fb.back = shield.shield2fb.backmax;
7255  break;
7256  case 4:
7257  a = shield.shield4fbrl.frontmax;
7258  b = shield.shield4fbrl.backmax;
7259  c = shield.shield4fbrl.leftmax;
7260  d = shield.shield4fbrl.rightmax;
7261  STDUPGRADE( shield.shield4fbrl.frontmax, up->shield.shield4fbrl.frontmax, templ->shield.shield4fbrl.frontmax, 0 );
7262  STDUPGRADE( shield.shield4fbrl.backmax, up->shield.shield4fbrl.backmax, templ->shield.shield4fbrl.backmax, 0 );
7263  STDUPGRADE( shield.shield4fbrl.leftmax, up->shield.shield4fbrl.leftmax, templ->shield.shield4fbrl.leftmax, 0 );
7264  STDUPGRADE( shield.shield4fbrl.rightmax, up->shield.shield4fbrl.rightmax, templ->shield.shield4fbrl.rightmax, 0 );
7265  if (a != shield.shield4fbrl.frontmax) shield.shield4fbrl.front = shield.shield4fbrl.frontmax;
7266  if (b != shield.shield4fbrl.backmax) shield.shield4fbrl.back = shield.shield4fbrl.backmax;
7267  if (c != shield.shield4fbrl.leftmax) shield.shield4fbrl.left = shield.shield4fbrl.leftmax;
7268  if (d != shield.shield4fbrl.rightmax) shield.shield4fbrl.right = shield.shield4fbrl.rightmax;
7269  break;
7270  case 8:
7271  a = shield.shield8.frontrighttopmax;
7272  b = shield.shield8.backrighttopmax;
7273  c = shield.shield8.frontlefttopmax;
7274  d = shield.shield8.backlefttopmax;
7275  aa = shield.shield8.frontrightbottommax;
7276  bb = shield.shield8.backrightbottommax;
7277  cc = shield.shield8.frontleftbottommax;
7278  dd = shield.shield8.backleftbottommax;
7279  STDUPGRADE( shield.shield8.frontrighttopmax,
7280  up->shield.shield8.frontrighttopmax,
7281  templ->shield.shield8.frontrighttopmax,
7282  0 );
7283  STDUPGRADE( shield.shield8.backrighttopmax,
7284  up->shield.shield8.backrighttopmax,
7285  templ->shield.shield8.backrighttopmax,
7286  0 );
7287  STDUPGRADE( shield.shield8.frontlefttopmax,
7288  up->shield.shield8.frontlefttopmax,
7289  templ->shield.shield8.frontlefttopmax,
7290  0 );
7291  STDUPGRADE( shield.shield8.backlefttopmax,
7292  up->shield.shield8.backlefttopmax,
7293  templ->shield.shield8.backlefttopmax,
7294  0 );
7295  STDUPGRADE( shield.shield8.frontrightbottommax,
7296  up->shield.shield8.frontrightbottommax,
7297  templ->shield.shield8.frontrightbottommax,
7298  0 );
7299  STDUPGRADE( shield.shield8.backrightbottommax,
7300  up->shield.shield8.backrightbottommax,
7301  templ->shield.shield8.backrightbottommax,
7302  0 );
7303  STDUPGRADE( shield.shield8.frontleftbottommax,
7304  up->shield.shield8.frontleftbottommax,
7305  templ->shield.shield8.frontleftbottommax,
7306  0 );
7307  STDUPGRADE( shield.shield8.backleftbottommax,
7308  up->shield.shield8.backleftbottommax,
7309  templ->shield.shield8.backleftbottommax,
7310  0 );
7311  if (a != shield.shield8.frontrighttopmax) shield.shield8.frontrighttop = shield.shield8.frontrighttopmax;
7312  if (b != shield.shield8.backrighttopmax) shield.shield8.backrighttop = shield.shield8.backrighttopmax;
7313  if (c != shield.shield8.frontlefttopmax) shield.shield8.frontlefttop = shield.shield8.frontlefttopmax;
7314  if (d != shield.shield8.backlefttopmax) shield.shield8.backlefttop = shield.shield8.backlefttopmax;
7315  if (aa != shield.shield8.frontrightbottommax)
7316  shield.shield8.frontrightbottom =
7317  shield.shield8.frontrightbottommax;
7318  if (bb != shield.shield8.backrightbottommax)
7319  shield.shield8.backrightbottom =
7320  shield.shield8.backrightbottommax;
7321  if (cc != shield.shield8.frontleftbottommax)
7322  shield.shield8.frontleftbottom =
7323  shield.shield8.frontleftbottommax;
7324  if (dd != shield.shield8.backleftbottommax) shield.shield8.backleftbottom = shield.shield8.backleftbottommax;
7325  break;
7326  }
7327  if (touchme && retval == UPGRADEOK)
7328  upgradedshield = true;
7329  } else if (up->FShieldData() > 0 || up->RShieldData() > 0 || up->LShieldData() > 0 || up->BShieldData() > 0) {
7330  cancompletefully = false;
7331  }
7332  }
7333  if (upgradedshield || upgradedrecharge) {
7334  if (up->shield.efficiency) {
7335  shield.efficiency = up->shield.efficiency;
7336  if (templ)
7337  if (shield.efficiency > templ->shield.efficiency)
7338  shield.efficiency = templ->shield.efficiency;
7339  }
7340  }
7341  if ( !csv_cell_null_check || force_change_on_nothing
7342  || cell_has_recursive_data( upgrade_name, up->faction, "Shield_Leak" ) ) {
7343  double myleak = 100-shield.leak;
7344  double upleak = 100-up->shield.leak;
7345  double templeak = 100-(templ != NULL ? templ->shield.leak : 0);
7346  bool ccf = cancompletefully;
7347  STDUPGRADE_SPECIFY_DEFAULTS( myleak, upleak, templeak, 0, 100, 100, false, shield.leak );
7348  if (touchme && myleak <= 100 && myleak >= 0) shield.leak = (char) 100-myleak;
7349  cancompletefully = ccf;
7350  }
7351  //DO NOT CHANGE see unit_customize.cpp
7352  static float lc = XMLSupport::parse_float( vs_config->getVariable( "physics", "lock_cone", ".8" ) );
7353  //DO NOT CHANGE! see unit.cpp:258
7354  static float tc = XMLSupport::parse_float( vs_config->getVariable( "physics", "autotracking", ".93" ) );
7355  static bool use_template_maxrange =
7356  XMLSupport::parse_bool( vs_config->getVariable( "physics", "use_upgrade_template_maxrange", "true" ) );
7357  //Radar stuff
7358  if ( !csv_cell_null_check || force_change_on_nothing
7359  || cell_has_recursive_data( upgrade_name, up->faction,
7360  "Radar_Range|Radar_Color|ITTS|Can_Lock|Max_Cone|Lock_Cone|Tracking_Cone" ) ) {
7361  if ( !csv_cell_null_check || force_change_on_nothing
7362  || cell_has_recursive_data( upgrade_name, up->faction, "Radar_Range" ) ) {
7363  STDUPGRADECLAMP( computer.radar.maxrange,
7364  up->computer.radar.maxrange,
7365  use_template_maxrange ? templ->computer.radar.maxrange : FLT_MAX,
7366  0 );
7367  }
7368  if ( !csv_cell_null_check || force_change_on_nothing
7369  || cell_has_recursive_data( upgrade_name, up->faction, "Radar_Color" ) )
7370  STDUPGRADE( computer.radar.capability, up->computer.radar.capability, templ->computer.radar.capability, 0 );
7371  if ( !csv_cell_null_check || force_change_on_nothing
7372  || cell_has_recursive_data( upgrade_name, up->faction, "ITTS" ) ) {
7373  computer.itts = UpgradeBoolval( computer.itts,
7374  up->computer.itts,
7375  touchme,
7376  downgrade,
7377  numave,
7378  percentage,
7379  force_change_on_nothing );
7380  }
7381  if ( !csv_cell_null_check || force_change_on_nothing
7382  || cell_has_recursive_data( upgrade_name, up->faction, "Can_Lock" ) ) {
7383  computer.radar.canlock = UpgradeBoolval( computer.radar.canlock,
7384  up->computer.radar.canlock,
7385  touchme,
7386  downgrade,
7387  numave,
7388  percentage,
7389  force_change_on_nothing );
7390  }
7391  //Do the two reversed ones below
7392  bool ccf = cancompletefully;
7393  if ( !csv_cell_null_check || force_change_on_nothing
7394  || cell_has_recursive_data( upgrade_name, up->faction, "Max_Cone" ) ) {
7395  double myleak = 1-computer.radar.maxcone;
7396  double upleak = 1-up->computer.radar.maxcone;
7397  double templeak = 1-(templ != NULL ? templ->computer.radar.maxcone : -1);
7398  STDUPGRADE_SPECIFY_DEFAULTS( myleak, upleak, templeak, 0, 0, 0, false, computer.radar.maxcone );
7399  if (touchme) computer.radar.maxcone = 1-myleak;
7400  }
7401  if (up->computer.radar.lockcone != lc) {
7402  double myleak = 1-computer.radar.lockcone;
7403  double upleak = 1-up->computer.radar.lockcone;
7404  double templeak = 1-(templ != NULL ? templ->computer.radar.lockcone : -1);
7405  if (templeak == 1-lc)
7406  templeak = 2;
7407  if ( !csv_cell_null_check || force_change_on_nothing
7408  || cell_has_recursive_data( upgrade_name, up->faction, "Lock_Cone" ) ) {
7409  STDUPGRADE_SPECIFY_DEFAULTS( myleak, upleak, templeak, 0, 0, 0, false, computer.radar.lockcone );
7410  if (touchme) computer.radar.lockcone = 1-myleak;
7411  }
7412  }
7413  if (up->computer.radar.trackingcone != tc) {
7414  double myleak = 1-computer.radar.trackingcone;
7415  double upleak = 1-up->computer.radar.trackingcone;
7416  double templeak = 1-(templ != NULL ? templ->computer.radar.trackingcone : -1);
7417  if (templeak == 1-tc)
7418  templeak = 2;
7419  if ( !csv_cell_null_check || force_change_on_nothing
7420  || cell_has_recursive_data( upgrade_name, up->faction, "Tracking_Cone" ) ) {
7421  STDUPGRADE_SPECIFY_DEFAULTS( myleak, upleak, templeak, 0, 0, 0, false, computer.radar.trackingcone );
7422  if (touchme) computer.radar.trackingcone = 1-myleak;
7423  }
7424  }
7425  cancompletefully = ccf;
7426  }
7427  //NO CLUE FOR BELOW
7428  if (downgrade) {
7429  if (jump.drive >= -1 && up->jump.drive >= -1) {
7430  if (touchme) jump.drive = -2;
7431  ++numave;
7432  percentage += .5*( (float) (100-jump.damage) )/(101-up->jump.damage);
7433  if (gen_downgrade_list)
7434  AddToDowngradeMap( up->name, up->jump.drive, ( (char*) &this->jump.drive )-( (char*) this ), tempdownmap );
7435  }
7436  if (cloaking != -1 && up->cloaking != -1) {
7437  if (touchme) cloaking = -1;
7438  ++numave;
7439  ++percentage;
7440  if (gen_downgrade_list)
7441  AddToDowngradeMap( up->name, up->cloaking, ( (char*) &this->cloaking )-( (char*) this ), tempdownmap );
7442  }
7443  //NOTE: Afterburner type 2 (jmp)
7444  //NOTE: Afterburner type 1 (gas)
7445  //NOTE: Afterburner type 0 (pwr)
7446  if (afterburnenergy < 32767 && afterburnenergy <= up->afterburnenergy && up->afterburnenergy != 32767
7447  && up->afterburnenergy != 0) {
7448  if (touchme) afterburnenergy = 32767, afterburntype = 0;
7449  ++numave;
7450  ++percentage;
7451  if (gen_downgrade_list) {
7452  AddToDowngradeMap( up->name,
7453  up->afterburntype,
7454  ( (char*) &this->afterburnenergy )-( (char*) this ),
7455  tempdownmap );
7456  }
7457  }
7458  } else {
7459  //we are upgrading!
7460  if (touchme) {
7461  for (unsigned int i = 0; i < up->pImage->cargo.size(); ++i)
7462  if ( CanAddCargo( up->pImage->cargo[i] ) )
7463  AddCargo( up->pImage->cargo[i], false );
7464  }
7465  if ( (cloaking == -1 && up->cloaking != -1) || force_change_on_nothing ) {
7466  if (touchme) {
7467  cloaking = up->cloaking;
7468  cloakmin = up->cloakmin;
7469  pImage->cloakrate = up->pImage->cloakrate;
7470  pImage->cloakglass = up->pImage->cloakglass;
7471  pImage->cloakenergy = up->pImage->cloakenergy;
7472  }
7473  ++numave;
7474  } else if (cloaking != -1 && up->cloaking != -1) {
7475  cancompletefully = false;
7476  }
7477  //NOTE: Afterburner type 2 (jmp)
7478  //NOTE: Afterburner type 1 (gas)
7479  //NOTE: Afterburner type 0 (pwr)
7480  if ( ( ( afterburnenergy > up->afterburnenergy
7481  || (afterburntype != up->afterburntype && up->afterburnenergy != 32767) )
7482  && up->afterburnenergy > 0 ) || force_change_on_nothing ) {
7483  ++numave;
7484  if (touchme) afterburnenergy = up->afterburnenergy, afterburntype = up->afterburntype;
7485  } else if (afterburnenergy <= up->afterburnenergy && afterburnenergy >= 0 && up->afterburnenergy > 0
7486  && up->afterburnenergy < 32767) {
7487  cancompletefully = false;
7488  }
7489  if ( (jump.drive == -2 && up->jump.drive >= -1) || force_change_on_nothing ) {
7490  if (touchme) {
7491  jump.drive = up->jump.drive;
7492  jump.damage = 0;
7493  }
7494  ++numave;
7495  } else if (jump.drive >= -1 && up->jump.drive >= -1) {
7496  cancompletefully = false;
7497  }
7498  }
7499  if (needs_redemption)
7500  if (!can_be_redeemed)
7501  cancompletefully = false;
7502  if (0 == numave) //Doesn't upgrade anything -- JS_NUDGE -- may want to revisit this later
7503  percentage = 1.0;
7504  if (numave)
7505  percentage = percentage/numave;
7506  if (0 && touchme && up->Mass && numave) {
7507  float multiplyer = ( (downgrade) ? -1 : 1 );
7508  Mass += multiplyer*percentage*up->Mass;
7509  if ( Mass < (templ ? templ->Mass : .000000001) )
7510  Mass = (templ ? templ->Mass : .000000001);
7511  Momentofinertia += multiplyer*percentage*up->Momentofinertia;
7512  if ( Momentofinertia < (templ ? templ->Momentofinertia : 0.00000001) )
7513  Momentofinertia = (templ ? templ->Momentofinertia : 0.00000001);
7514  }
7515  if (gen_downgrade_list) {
7516  float MyPercentMin = ComputeMinDowngradePercent();
7517  if (downgrade && percentage > MyPercentMin)
7518  for (vsUMap< int, DoubleName >::iterator i = tempdownmap.begin(); i != tempdownmap.end(); ++i)
7519  downgrademap[(*i).first] = (*i).second;
7520  }
7521  return cancompletefully;
7522 }
7523 
7524 #undef STDUPGRADECLAMP
7525 #undef STDUPGRADE
7526 #undef STDUPGRADE_SPECIFY_DEFAULTS
7527 
7529 {
7530  vector< Cargo >savedCargo;
7531  savedCargo.swap( pImage->cargo );
7532  vector< Mount >savedWeap;
7533  savedWeap.swap( mounts );
7534  const Unit *temprate = makeFinalBlankUpgrade( name, faction );
7535  bool success = false;
7536  double pct = 0;
7537  if ( temprate && temprate->name != string( "LOAD_FAILED" ) ) {
7538  success = Upgrade( temprate, -1, -1, 0, true, pct, NULL, true );
7539  if (pct > 0)
7540  success = true;
7541  }
7542  savedCargo.swap( pImage->cargo );
7543  savedWeap.swap( mounts );
7544  return success;
7545 }
7546 
7548 {
7549  if ( whichmount < 0 || (unsigned int) whichmount >= mounts.size() ) return Vector( -1, -1, -1 );
7550  return Vector( mounts[whichmount].functionality,
7551  mounts[whichmount].maxfunctionality,
7552  ( (mounts[whichmount].status == Mount::ACTIVE || mounts[whichmount].status
7553  == Mount::INACTIVE) ? 0.0 : (Mount::UNCHOSEN ? 2.0 : 1.0) ) );
7554 }
7555 
7557 {
7558  int cost = 1;
7559  unsigned int i;
7560  for (i = 0; i < (1+MAXVDUS+UnitImages< void >::NUMGAUGES)*2; ++i)
7561  if (pImage->cockpit_damage[i] < 1)
7562  ++cost;
7563  if (pImage->fireControlFunctionality < 1)
7564  ++cost;
7565  if (pImage->fireControlFunctionalityMax < 1)
7566  ++cost;
7567  if (pImage->SPECDriveFunctionality < 1)
7568  ++cost;
7569  if (pImage->SPECDriveFunctionalityMax < 1)
7570  ++cost;
7571  if (pImage->CommFunctionality < 1)
7572  ++cost;
7573  if (pImage->CommFunctionalityMax < 1)
7574  ++cost;
7575  if (pImage->LifeSupportFunctionality < 1)
7576  ++cost;
7577  if (pImage->LifeSupportFunctionalityMax < 1)
7578  ++cost;
7579  for (i = 0; i < numCargo(); ++i)
7580  if (GetCargo( i ).GetCategory().find( DamagedCategory ) == 0)
7581  ++cost;
7582  return cost;
7583 }
7584 
7586 {
7587  vector< Cargo >savedCargo;
7588  savedCargo.swap( pImage->cargo );
7589  vector< Mount >savedWeap;
7590  savedWeap.swap( mounts );
7591  int upfac = FactionUtil::GetUpgradeFaction();
7592  const Unit *temprate = makeFinalBlankUpgrade( name, faction );
7593  int success = 0;
7594  double pct = 0;
7595  if ( temprate && temprate->name != string( "LOAD_FAILED" ) ) {
7596  success = Upgrade( temprate, -1, -1, 0, false, pct, NULL, false ) ? 1 : 0;
7597  if (pct > 0)
7598  success = 1;
7599  }
7600  savedCargo.swap( pImage->cargo );
7601  savedWeap.swap( mounts );
7602  UnitImages< void > *im = &GetImageInformation();
7603  for (int i = 0; i < (1+MAXVDUS+UnitImages< void >::NUMGAUGES)*2; ++i)
7604  if (im->cockpit_damage[i] < 1) {
7605  im->cockpit_damage[i] = 1;
7606  success += 1;
7607  pct = 1;
7608  }
7609  if (im->fireControlFunctionality < 1) {
7610  im->fireControlFunctionality = 1;
7611  pct = 1;
7612  success += 1;
7613  }
7614  if (im->fireControlFunctionalityMax < 1) {
7616  pct = 1;
7617  success += 1;
7618  }
7619  if (im->SPECDriveFunctionality < 1) {
7620  im->SPECDriveFunctionality = 1;
7621  pct = 1;
7622  success += 1;
7623  }
7624  if (im->SPECDriveFunctionalityMax < 1) {
7625  im->SPECDriveFunctionalityMax = 1;
7626  pct = 1;
7627  success += 1;
7628  }
7629  if (im->CommFunctionality < 1) {
7630  im->CommFunctionality = 1;
7631  pct = 1;
7632  success += 1;
7633  }
7634  if (im->CommFunctionalityMax < 1) {
7635  im->CommFunctionalityMax = 1;
7636  pct = 1;
7637  success += 1;
7638  }
7639  if (im->LifeSupportFunctionality < 1) {
7640  im->LifeSupportFunctionality = 1;
7641  pct = 1;
7642  success += 1;
7643  }
7644  if (im->LifeSupportFunctionalityMax < 1) {
7646  pct = 1;
7647  success += 1;
7648  }
7649  damages = NO_DAMAGE;
7650  bool ret = success && pct > 0;
7651  static bool ComponentBasedUpgrades =
7652  XMLSupport::parse_bool( vs_config->getVariable( "physics", "component_based_upgrades", "false" ) );
7653  if (ComponentBasedUpgrades) {
7654  for (unsigned int i = 0; i < numCargo(); ++i)
7655  if (GetCargo( i ).GetCategory().find( DamagedCategory ) == 0) {
7656  ++success;
7657  static int damlen = strlen( DamagedCategory );
7658  GetCargo( i ).category = "upgrades/"+GetCargo( i ).GetCategory().substr( damlen );
7659  }
7660  } else if (ret) {
7661  const Unit *maxrecharge = makeTemplateUpgrade( name.get()+".template", faction );
7662 
7664  for (unsigned int i = 0; i < mpl->numCargo(); ++i)
7665  if (mpl->GetCargo( i ).GetCategory().find( "upgrades" ) == 0) {
7666  const Unit *up = loadUnitByCache( mpl->GetCargo( i ).content, upfac );
7667  //now we analyzify up!
7668  if (up->MaxShieldVal() == MaxShieldVal() && up->shield.recharge > shield.recharge) {
7669  shield.recharge = up->shield.recharge;
7670  if (maxrecharge)
7671  if (shield.recharge > maxrecharge->shield.recharge)
7672  shield.recharge = maxrecharge->shield.recharge;
7673  }
7674  if (up->maxenergy == maxenergy && up->recharge > recharge) {
7675  recharge = up->recharge;
7676  if (recharge > maxrecharge->recharge)
7677  recharge = maxrecharge->recharge;
7678  }
7679  }
7680  }
7681  return success;
7682 }
7683 
7684 float RepairPrice( float operational, float price )
7685 {
7686  return .5*price*(1-operational)*g_game.difficulty;
7687 }
7688 
7689 extern bool isWeapon( std::string name );
7690 
7691 //item must be non-null... but baseUnit or credits may be NULL.
7692 bool Unit::RepairUpgradeCargo( Cargo *item, Unit *baseUnit, float *credits )
7693 {
7694  assert( (item!=NULL) |! "Unit::RepairUpgradeCargo got a null item." ); //added by chuck_starchaser
7695  double itemPrice = baseUnit ? baseUnit->PriceCargo( item->content ) : item->price;
7696  if ( isWeapon( item->category ) ) {
7697  const Unit *upgrade = getUnitFromUpgradeName( item->content, this->faction );
7698  if ( upgrade->GetNumMounts() ) {
7699  double price = itemPrice; //RepairPrice probably won't work for mounts.
7700  if ( !credits || price <= (*credits) ) {
7701  if (credits) (*credits) -= price;
7702  const Mount *mnt = &upgrade->mounts[0];
7703  unsigned int nummounts = this->GetNumMounts();
7704  bool complete = false;
7705  for (unsigned int i = 0; i < nummounts; ++i)
7706  if (mnt->type->weapon_name == this->mounts[i].type->weapon_name) {
7707  if (this->mounts[i].status == Mount::DESTROYED) {
7708  this->mounts[i].status = Mount::INACTIVE;
7709  complete = true;
7710  }
7711  if (this->mounts[i].functionality < 1.0f) {
7712  this->mounts[i].functionality = 1.0f;
7713  complete = true;
7714  }
7715  if (this->mounts[i].maxfunctionality < 1.0f) {
7716  this->mounts[i].maxfunctionality = 1.0f;
7717  complete = true;
7718  }
7719  if (complete) break;
7720  }
7721  return complete;
7722  }
7723  }
7724  return false;
7725  } else {
7726  Cargo sold;
7727  bool notadditive = (item->GetContent().find( "add_" ) != 0 && item->GetContent().find( "mult_" ) != 0);
7728  if (notadditive || item->GetCategory().find( DamagedCategory ) == 0) {
7729  Cargo itemCopy = *item; //Copy this because we reload master list before we need it.
7730  const Unit *un = getUnitFromUpgradeName( item->content, this->faction );
7731  if (un) {
7732  double percentage = UnitUtil::PercentOperational( this, item->content, item->category, false );
7733  double price = RepairPrice( percentage, itemPrice );
7734  if ( !credits || price <= (*credits) ) {
7735  if (credits)
7736  (*credits) -= price;
7737  if (notadditive)
7738  this->Upgrade( un, 0, 0, 0, true, percentage, makeTemplateUpgrade( this->name, this->faction ) );
7739  if (item->GetCategory().find( DamagedCategory ) == 0) {
7740  unsigned int where;
7741  Cargo *c = this->GetCargo( item->content, where );
7742  if (c)
7743  c->category = "upgrades/"+c->GetCategory().substr( strlen( DamagedCategory ) );
7744  }
7745  return true;
7746  }
7747  }
7748  }
7749  }
7750  return false;
7751 }
7752 
7753 /*
7754  **********************************************************************************
7755  **** UNIT_CARGO STUFF
7756  **********************************************************************************
7757  */
7758 
7759 /***************** UNCOMMENT GETMASTERPARTLIST WHEN MODIFIED FACTION STUFF !!!!!! */
7760 
7761 float Unit::PriceCargo( const std::string &s )
7762 {
7763  Cargo tmp;
7764  tmp.content = s;
7765  vector< Cargo >::iterator mycargo = std::find( pImage->cargo.begin(), pImage->cargo.end(), tmp );
7766  if ( mycargo == pImage->cargo.end() ) {
7768  if (this != mpl) {
7769  return mpl->PriceCargo( s );
7770  } else {
7771  static float spacejunk = parse_float( vs_config->getVariable( "cargo", "space_junk_price", "10" ) );
7772  return spacejunk;
7773  }
7774  }
7775  float price;
7776  price = (*mycargo).price;
7777  return price;
7778 }
7779 
7780 static const GFXColor disable( 1, 0, 0, 1 );
7781 extern int GetModeFromName( const char* );
7782 extern double ComputeMinDowngradePercent();
7783 
7784 vector< CargoColor >& Unit::FilterDowngradeList( vector< CargoColor > &mylist, bool downgrade )
7785 {
7786  const Unit *templ = NULL;
7787  const Unit *downgradelimit = NULL;
7788  static bool staticrem =
7789  XMLSupport::parse_bool( vs_config->getVariable( "general", "remove_impossible_downgrades", "true" ) );
7790  static float MyPercentMin = ComputeMinDowngradePercent();
7791  int upgrfac = FactionUtil::GetUpgradeFaction();
7792  for (unsigned int i = 0; i < mylist.size(); ++i) {
7793  bool removethis = true /*staticrem*/;
7794  int mode = GetModeFromName( mylist[i].cargo.GetContent().c_str() );
7795  if ( mode != 2 || (!downgrade) ) {
7796  const Unit *NewPart = UnitConstCache::getCachedConst( StringIntKey( mylist[i].cargo.GetContent().c_str(), upgrfac ) );
7797  if (!NewPart) {
7799  mylist[i].cargo.GetContent(),
7800  upgrfac ),
7801  UnitFactory::createUnit( mylist[i].cargo.GetContent().c_str(), false,
7802  upgrfac ) );
7803  }
7804  if ( NewPart->name == string( "LOAD_FAILED" ) ) {
7805  const Unit *NewPart =
7806  UnitConstCache::getCachedConst( StringIntKey( mylist[i].cargo.GetContent().c_str(), faction ) );
7807  if (!NewPart) {
7808  NewPart = UnitConstCache::setCachedConst( StringIntKey( mylist[i].cargo.content, faction ),
7809  UnitFactory::createUnit( mylist[i].cargo.GetContent().c_str(),
7810  false, faction ) );
7811  }
7812  }
7813  if ( NewPart->name != string( "LOAD_FAILED" ) ) {
7814  int maxmountcheck = NewPart->GetNumMounts() ? GetNumMounts() : 1;
7815  char *unitdir = GetUnitDir( name.get().c_str() );
7816  string templnam = string( unitdir )+".template";
7817  string limiternam = string( unitdir )+".blank";
7818  if (!downgrade) {
7819  templ = UnitConstCache::getCachedConst( StringIntKey( templnam, faction ) );
7820  if (templ == NULL) {
7821  templ =
7823  faction ),
7824  UnitFactory::createUnit( templnam.c_str(), true, this->faction ) );
7825  }
7826  if ( templ->name == std::string( "LOAD_FAILED" ) )
7827  templ = NULL;
7828  } else {
7829  downgradelimit = UnitConstCache::getCachedConst( StringIntKey( limiternam, faction ) );
7830  if (downgradelimit == NULL) {
7831  downgradelimit = UnitConstCache::setCachedConst( StringIntKey( limiternam,
7832  faction ),
7833  UnitFactory::createUnit( limiternam.c_str(), true,
7834  this->faction ) );
7835  }
7836  if ( downgradelimit->name == std::string( "LOAD_FAILED" ) )
7837  downgradelimit = NULL;
7838  }
7839  free( unitdir );
7840  for (int m = 0; m < maxmountcheck; ++m) {
7841  int s = 0;
7842  for (un_iter ui = getSubUnits(); s == 0 || ( (*ui) != NULL ); ++ui, ++s) {
7843  double percent = 1;
7844  if (downgrade) {
7845  if ( canDowngrade( NewPart, m, s, percent, downgradelimit ) ) {
7846  if (percent > MyPercentMin) {
7847  removethis = false;
7848  break;
7849  }
7850  }
7851  } else if ( canUpgrade( NewPart, m, s, mode, false /*force*/, percent, templ ) ) {
7852  removethis = false;
7853  break;
7854  }
7855  if (*ui == NULL)
7856  break;
7857  }
7858  }
7859  }
7860  } else {
7861  removethis = true;
7862  }
7863  if (removethis) {
7864  if (downgrade && staticrem) {
7865  mylist.erase( mylist.begin()+i );
7866  i--;
7867  } else {
7868  mylist[i].color = disable;
7869  }
7870  }
7871  }
7872  return mylist;
7873 }
7874 
7875 vector< CargoColor >& Unit::FilterUpgradeList( vector< CargoColor > &mylist )
7876 {
7877  static bool filtercargoprice = XMLSupport::parse_bool( vs_config->getVariable( "cargo", "filter_expensive_cargo", "false" ) );
7878  if (filtercargoprice) {
7879  Cockpit *cp = _Universe->isPlayerStarship( this );
7880  if (cp) {
7881  for (unsigned int i = 0; i < mylist.size(); ++i)
7882  if (mylist[i].cargo.price > cp->credits) {
7883  mylist[i].color = disable;
7884  }
7885  }
7886  }
7887  return FilterDowngradeList( mylist, false );
7888 }
7889 
7890 bool Unit::IsBase() const
7891 {
7892  return ((flightgroup != NULL) && (flightgroup->name == "Base"));
7893 }
7894 
7895 inline float uniformrand( float min, float max )
7896 {
7897  return ( (float) ( rand() )/RAND_MAX )*(max-min)+min;
7898 }
7899 
7900 inline QVector randVector( float min, float max )
7901 {
7902  return QVector( uniformrand( min, max ),
7903  uniformrand( min, max ),
7904  uniformrand( min, max ) );
7905 }
7906 
7908 {
7909  turretstatus = 3;
7910  Unit *un;
7911  for (un_iter iter = getSubUnits(); (un = *iter); ++iter) {
7912  if ( !CheckAccessory( un ) ) {
7913  un->EnqueueAIFirst( new Orders::FireAt( 15.0f ) );
7914  un->EnqueueAIFirst( new Orders::FaceTarget( false, 3 ) );
7915  }
7916  un->TurretFAW();
7917  }
7918 }
7919 
7920 extern int SelectDockPort( Unit*, Unit *parent );
7921 
7922 //index in here is unsigned, UINT_MAX and UINT_MAX-1 seem to be
7923 //special states. This means the total amount of cargo any ship can have
7924 //is UINT_MAX -3 which is 65532 for 32bit machines.
7925 void Unit::EjectCargo( unsigned int index )
7926 {
7927  Cargo *tmp = NULL;
7928  Cargo ejectedPilot;
7929  Cargo dockedPilot;
7930  string name;
7931  bool isplayer = false;
7932  //if (index==((unsigned int)-1)) { is ejecting normally
7933  //if (index==((unsigned int)-2)) { is ejecting for eject-dock
7934 
7935  Cockpit *cp = NULL;
7936  if ( index == (UINT_MAX-1) ) {
7937  int pilotnum = _Universe->CurrentCockpit();
7938  //this calls the unit's existence, by the way.
7939  name = "return_to_cockpit";
7940  if ( NULL != ( cp = _Universe->isPlayerStarship( this ) ) ) {
7941  isplayer = true;
7942  string playernum = string( "player" )+( (pilotnum == 0) ? string( "" ) : XMLSupport::tostring( pilotnum ) );
7943  }
7944  //we will have to check for this on undock to return to the parent unit!
7945  dockedPilot.content = "return_to_cockpit";
7946  dockedPilot.mass = .1;
7947  dockedPilot.volume = 1;
7948  tmp = &dockedPilot;
7949  }
7950  if (index == UINT_MAX) {
7951  int pilotnum = _Universe->CurrentCockpit();
7952  name = "Pilot";
7953  if ( NULL != ( cp = _Universe->isPlayerStarship( this ) ) ) {
7954  string playernum = string( "player" )+( (pilotnum == 0) ? string( "" ) : XMLSupport::tostring( pilotnum ) );
7955  isplayer = true;
7956  }
7957  ejectedPilot.content = "eject";
7958  ejectedPilot.mass = .1;
7959  ejectedPilot.volume = 1;
7960  tmp = &ejectedPilot;
7961  }
7962  if ( index < numCargo() )
7963  tmp = &GetCargo( index );
7964  static float cargotime = XMLSupport::parse_float( vs_config->getVariable( "physics", "cargo_live_time", "600" ) );
7965  if (tmp) {
7966  string tmpcontent = tmp->content;
7967  if (tmp->mission)
7968  tmpcontent = "Mission_Cargo";
7969  const int ulen = strlen( "upgrades" );
7970  //prevents a number of bad things, incl. impossible speeds and people getting rich on broken stuff
7971  if ( (!tmp->mission) && memcmp( tmp->GetCategory().c_str(), "upgrades", ulen ) == 0 )
7972  tmpcontent = "Space_Salvage";
7973  //this happens if it's a ship
7974  if (tmp->quantity > 0) {
7975  const int sslen = strlen( "starships" );
7976  Unit *cargo = NULL;
7977  if (tmp->GetCategory().length() >= (unsigned int) sslen) {
7978  if ( (!tmp->mission) && memcmp( tmp->GetCategory().c_str(), "starships", sslen ) == 0 ) {
7979  string ans = tmpcontent;
7980  string::size_type blank = ans.find( ".blank" );
7981  if (blank != string::npos)
7982  ans = ans.substr( 0, blank );
7983  Flightgroup *fg = this->getFlightgroup();
7984  int fgsnumber = 0;
7985  if (fg != NULL) {
7986  fgsnumber = fg->nr_ships;
7987  ++(fg->nr_ships);
7988  ++(fg->nr_ships_left);
7989  }
7990  cargo = UnitFactory::createUnit( ans.c_str(), false, faction, "", fg, fgsnumber, NULL, getUniqueSerial() );
7991  cargo->PrimeOrders();
7992  cargo->SetAI( new Orders::AggressiveAI( "default.agg.xml" ) );
7993  cargo->SetTurretAI();
7994  //he's alive!!!!!
7995  }
7996  }
7997  float arot = 0;
7998  static float grot =
7999  XMLSupport::parse_float( vs_config->getVariable( "graphics", "generic_cargo_rotation_speed",
8000  "1" ) )*3.1415926536/180;
8001  if (!cargo) {
8002  static float crot =
8003  XMLSupport::parse_float( vs_config->getVariable( "graphics", "cargo_rotation_speed",
8004  "60" ) )*3.1415926536/180;
8005  static float erot =
8006  XMLSupport::parse_float( vs_config->getVariable( "graphics", "eject_rotation_speed",
8007  "0" ) )*3.1415926536/180;
8008  if (tmpcontent == "eject") {
8009  if (isplayer) {
8010  Flightgroup *fg = this->getFlightgroup();
8011  int fgsnumber = 0;
8012  if (fg != NULL) {
8013  fgsnumber = fg->nr_ships;
8014  ++(fg->nr_ships);
8015  ++(fg->nr_ships_left);
8016  }
8017  cargo = UnitFactory::createUnit( "eject", false, faction, "", fg, fgsnumber, NULL, getUniqueSerial() );
8018  } else {
8019  int fac = FactionUtil::GetUpgradeFaction();
8020  cargo = UnitFactory::createUnit( "eject", false, fac, "", NULL, 0, NULL, getUniqueSerial() );
8021  }
8022  if (owner)
8023  cargo->owner = owner;
8024  else
8025  cargo->owner = this;
8026  arot = erot;
8027  static bool eject_attacks = XMLSupport::parse_bool( vs_config->getVariable( "AI", "eject_attacks", "false" ) );
8028  if (eject_attacks) {
8029  cargo->PrimeOrders();
8030  //generally fraidycat AI
8031  cargo->SetAI( new Orders::AggressiveAI( "default.agg.xml" ) );
8032  }
8033 
8034  //Meat. Docking should happen here
8035  } else if (tmpcontent == "return_to_cockpit") {
8036  if (isplayer) {
8037  Flightgroup *fg = this->getFlightgroup();
8038  int fgsnumber = 0;
8039  if (fg != NULL) {
8040  fgsnumber = fg->nr_ships;
8041  ++(fg->nr_ships);
8042  ++(fg->nr_ships_left);
8043  }
8044  cargo = UnitFactory::createUnit( "return_to_cockpit", false, faction, "", fg, fgsnumber, NULL,
8045  getUniqueSerial() );
8046  if (owner)
8047  cargo->owner = owner;
8048  else
8049  cargo->owner = this;
8050  } else {
8051  int fac = FactionUtil::GetUpgradeFaction();
8052  static float ejectcargotime =
8053  XMLSupport::parse_float( vs_config->getVariable( "physics", "eject_live_time", SERVER ? "200" : "0" ) );
8054  if (cargotime == 0.0) {
8055  cargo = UnitFactory::createUnit( "eject", false, fac, "", NULL, 0, NULL, getUniqueSerial() );
8056  } else {
8057  cargo = UnitFactory::createMissile( "eject",
8058  fac, "",
8059  0,
8060  0,
8061  ejectcargotime,
8062  1,
8063  1,
8064  1,
8065  getUniqueSerial() );
8066  }
8067  }
8068  arot = erot;
8069  cargo->PrimeOrders();
8070  cargo->aistate = NULL;
8071  } else {
8072  string tmpnam = tmpcontent+".cargo";
8073  static std::string nam( "Name" );
8074  float rot = crot;
8075  if (UniverseUtil::LookupUnitStat( tmpnam, "upgrades", nam ).length() == 0) {
8076  tmpnam = "generic_cargo";
8077  rot = grot;
8078  }
8079  int upgrfac = FactionUtil::GetUpgradeFaction();
8080  cargo = UnitFactory::createMissile( tmpnam.c_str(),
8081  upgrfac,
8082  "",
8083  0,
8084  0,
8085  cargotime,
8086  1,
8087  1,
8088  1,
8089  getUniqueSerial()
8090  );
8091  arot = rot;
8092  }
8093  }
8094  if (cargo->name == "LOAD_FAILED") {
8095  cargo->Kill();
8096  cargo = UnitFactory::createMissile( "generic_cargo",
8098  0,
8099  0,
8100  cargotime,
8101  1,
8102  1,
8103  1,
8104  getUniqueSerial() );
8105  arot = grot;
8106  }
8107  Vector rotation( vsrandom.uniformInc( -arot, arot ), vsrandom.uniformInc( -arot, arot ), vsrandom.uniformInc( -arot,
8108  arot ) );
8109  static bool all_rotate_same =
8110  XMLSupport::parse_bool( vs_config->getVariable( "graphics", "cargo_rotates_at_same_speed", "true" ) );
8111  if (all_rotate_same && arot != 0) {
8112  float tmp = rotation.Magnitude();
8113  if (tmp > .001) {
8114  rotation.Scale( 1/tmp );
8115  rotation *= arot;
8116  }
8117  }
8118  if ( 0 && cargo->rSize() >= rSize() ) {
8119  cargo->Kill();
8120  } else {
8121  Vector tmpvel = -Velocity;
8122  if (tmpvel.MagnitudeSquared() < .00001) {
8123  tmpvel = randVector( -rSize(), rSize() ).Cast();
8124  if (tmpvel.MagnitudeSquared() < .00001)
8125  tmpvel = Vector( 1, 1, 1 );
8126  }
8127  tmpvel.Normalize();
8128  if ( (SelectDockPort( this, this ) > -1) ) {
8129  static float eject_cargo_offset =
8130  XMLSupport::parse_float( vs_config->getVariable( "physics", "eject_distance", "20" ) );
8131  QVector loc( Transform( this->GetTransformation(), this->DockingPortLocations()[0].GetPosition().Cast() ) );
8132  //index is always > -1 because it's unsigned. Lets use the correct terms, -1 in Uint is UINT_MAX
8133  loc += tmpvel*1.5*rSize()+randVector( -.5*rSize()+(index == UINT_MAX ? eject_cargo_offset/2 : 0),
8134  .5*rSize()+(index == UINT_MAX ? eject_cargo_offset : 0) );
8135  cargo->SetPosAndCumPos( loc );
8136  Vector p, q, r;
8137  this->GetOrientation( p, q, r );
8138  cargo->SetOrientation( p, q, r );
8139  if (owner)
8140  cargo->owner = owner;
8141  else
8142  cargo->owner = this;
8143  } else {
8144  cargo->SetPosAndCumPos( Position()+tmpvel*1.5*rSize()+randVector( -.5*rSize(), .5*rSize() ) );
8145  cargo->SetAngularVelocity( rotation );
8146  }
8147  static float velmul = XMLSupport::parse_float( vs_config->getVariable( "physics", "eject_cargo_speed", "1" ) );
8148  cargo->SetOwner( this );
8149  cargo->SetVelocity( Velocity*velmul+randVector( -.25, .25 ).Cast() );
8150  cargo->Mass = tmp->mass;
8151  if (name.length() > 0)
8152  cargo->name = name;
8153  else if (tmp)
8154  cargo->name = tmpcontent;
8155  if (cp && _Universe->numPlayers() == 1) {
8156  cargo->SetOwner( NULL );
8157  PrimeOrders();
8158  cargo->SetTurretAI();
8159  cargo->faction = faction;
8160  //changes control to that cockpit
8161  cp->SetParent( cargo, "", "", Position() );
8162  if (tmpcontent == "return_to_cockpit") {
8163  static bool simulate_while_at_base =
8164  XMLSupport::parse_bool( vs_config->getVariable( "physics", "simulate_while_docked", "false" ) );
8165  if ( (simulate_while_at_base) || (_Universe->numPlayers() > 1) )
8166  this->TurretFAW();
8167  //make unit a sitting duck in the mean time
8168  SwitchUnits( NULL, this );
8169  if (owner)
8170  cargo->owner = owner;
8171  else
8172  cargo->owner = this;
8173  PrimeOrders();
8174  cargo->SetOwner( this );
8175  cargo->Position() = this->Position();
8176  cargo->SetPosAndCumPos( this->Position() );
8177  //claims to be docked, stops speed and taking damage etc. but doesn't seem to call the base script
8178  cargo->ForceDock( this, 0 );
8179  abletodock( 3 );
8180  //actually calls the interface, meow. yay!
8181  cargo->UpgradeInterface( this );
8182  if ( (simulate_while_at_base) || (_Universe->numPlayers() > 1) )
8183  this->TurretFAW();
8184  } else {
8185  SwitchUnits( NULL, cargo );
8186  if (owner)
8187  cargo->owner = owner;
8188  else
8189  cargo->owner = this;
8190  } //switching NULL gives "dead" ai to the unit I ejected from, by the way.
8191  }
8192  _Universe->activeStarSystem()->AddUnit( cargo );
8193  if ( (unsigned int) index != ( (unsigned int) -1 ) && (unsigned int) index != ( (unsigned int) -2 ) )
8194  if ( index < pImage->cargo.size() )
8195  RemoveCargo( index, 1, true );
8196  }
8197  }
8198  }
8199 }
8200 
8201 int Unit::RemoveCargo( unsigned int i, int quantity, bool eraseZero )
8202 {
8203  if ( !( i < pImage->cargo.size() ) ) {
8204  fprintf( stderr, "(previously) FATAL problem...removing cargo that is past the end of array bounds." );
8205  return 0;
8206  }
8207  Cargo *carg = &(pImage->cargo[i]);
8208  if (quantity > carg->quantity)
8209  quantity = carg->quantity;
8210  if ( Network && !_Universe->netLocked() ) {
8211  int playernum = _Universe->whichPlayerStarship( this );
8212  if (playernum >= 0)
8213  Network[playernum].cargoRequest( 0, this->serial, carg->GetContent(), quantity, 0, 0 );
8214  else
8215  return 0;
8216  return 0;
8217  }
8218  static bool usemass = XMLSupport::parse_bool( vs_config->getVariable( "physics", "use_cargo_mass", "true" ) );
8219  if (usemass)
8220  Mass -= quantity*carg->mass;
8221  if ( SERVER && !_Universe->netLocked() && getStarSystem() ) {
8222  VSServer->BroadcastCargoUpgrade( this->serial, 0, this->serial, carg->GetContent(),
8223  carg->price, carg->mass, carg->volume, carg->mission,
8224  quantity, 0, 0, getStarSystem()->GetZone() );
8225  }
8226  carg->quantity -= quantity;
8227  if (carg->quantity <= 0 && eraseZero)
8228  pImage->cargo.erase( pImage->cargo.begin()+i );
8229  return quantity;
8230 }
8231 
8232 void Unit::AddCargo( const Cargo &carg, bool sort )
8233 {
8234  if ( Network && !_Universe->netLocked() ) {
8235  int playernum = _Universe->whichPlayerStarship( this );
8236  if (playernum >= 0)
8237  Network[playernum].cargoRequest( this->serial, 0, carg.GetContent(), carg.quantity, 0, 0 );
8238  else
8239  return;
8240  return;
8241  }
8242  if ( SERVER && !_Universe->netLocked() && getStarSystem() ) {
8243  VSServer->BroadcastCargoUpgrade( this->serial, this->serial, 0, carg.GetContent(),
8244  carg.price, carg.mass, carg.volume, carg.mission,
8245  carg.quantity, 0, 0, getStarSystem()->GetZone() );
8246  }
8247  static bool usemass = XMLSupport::parse_bool( vs_config->getVariable( "physics", "use_cargo_mass", "true" ) );
8248  if (usemass)
8249  Mass += carg.quantity*carg.mass;
8250  pImage->cargo.push_back( carg );
8251  if (sort)
8252  SortCargo();
8253 }
8254 
8255 bool cargoIsUpgrade( const Cargo &c )
8256 {
8257  return c.GetCategory().find( "upgrades" ) == 0;
8258 }
8259 
8261 {
8262  return pImage->HiddenCargoVolume;
8263 }
8264 
8265 bool Unit::CanAddCargo( const Cargo &carg ) const
8266 {
8267  //Always can, in this case (this accounts for some odd precision issues)
8268  if ( (carg.quantity == 0) || (carg.volume == 0) )
8269  return true;
8270  //Test volume availability
8271  bool upgradep = cargoIsUpgrade( carg );
8272  float total_volume = carg.quantity*carg.volume+( upgradep ? getUpgradeVolume() : getCargoVolume() );
8273  if ( total_volume <= ( upgradep ? getEmptyUpgradeVolume() : getEmptyCargoVolume() ) )
8274  return true;
8275  //Hm... not in main unit... perhaps a subunit can take it
8276  const Unit *un;
8277  for (un_kiter i = viewSubUnits(); (un = *i) != NULL; ++i)
8278  if ( un->CanAddCargo( carg ) )
8279  return true;
8280  //Bad luck
8281  return false;
8282 }
8283 
8284 //The cargo volume of this ship when empty. Max cargo volume.
8285 float Unit::getEmptyCargoVolume( void ) const
8286 {
8287  return pImage->CargoVolume;
8288 }
8289 
8290 float Unit::getEmptyUpgradeVolume( void ) const
8291 {
8292  return pImage->UpgradeVolume;
8293 }
8294 
8295 float Unit::getCargoVolume( void ) const
8296 {
8297  float result = 0.0;
8298  for (unsigned int i = 0; i < pImage->cargo.size(); ++i)
8299  if ( !cargoIsUpgrade( pImage->cargo[i] ) )
8300  result += pImage->cargo[i].quantity*pImage->cargo[i].volume;
8301  return result;
8302 }
8303 
8304 float Unit::getUpgradeVolume( void ) const
8305 {
8306  float result = 0.0;
8307  for (unsigned int i = 0; i < pImage->cargo.size(); ++i)
8308  if ( cargoIsUpgrade( pImage->cargo[i] ) )
8309  result += pImage->cargo[i].quantity*pImage->cargo[i].volume;
8310  return result;
8311 }
8312 
8314 {
8315  return *pImage;
8316 }
8317 
8318 Cargo& Unit::GetCargo( unsigned int i )
8319 {
8320  return pImage->cargo[i];
8321 }
8322 
8323 const Cargo& Unit::GetCargo( unsigned int i ) const
8324 {
8325  return pImage->cargo[i];
8326 }
8327 
8329 {
8330 public:
8331  bool operator()( const Cargo &a, const Cargo &b )
8332  {
8333  std::string::const_iterator aiter = a.GetCategory().begin();
8334  std::string::const_iterator aend = a.GetCategory().end();
8335  std::string::const_iterator biter = b.GetCategory().begin();
8336  std::string::const_iterator bend = b.GetCategory().end();
8337  for (; aiter != aend && biter != bend; ++aiter, ++biter) {
8338  char achar = *aiter;
8339  char bchar = *biter;
8340  if (achar < bchar)
8341  return true;
8342  if (achar > bchar)
8343  return false;
8344  }
8345  return false;
8346  }
8347 };
8348 
8349 void Unit::GetSortedCargoCat( const std::string &cat, size_t &begin, size_t &end )
8350 {
8351  vector< Cargo >::iterator Begin = pImage->cargo.begin();
8352  vector< Cargo >::iterator End = pImage->cargo.end();
8353  vector< Cargo >::iterator lbound = pImage->cargo.end();
8354  vector< Cargo >::iterator ubound = pImage->cargo.end();
8355 
8356  Cargo beginningtype;
8357  beginningtype.category = cat;
8358  CatCompare Comp;
8359  lbound = std::lower_bound( Begin, End, beginningtype, Comp );
8360  beginningtype.content = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
8361  ubound = std::upper_bound( Begin, End, beginningtype, Comp );
8362  begin = lbound-Begin;
8363  end = ubound-Begin;
8364 }
8365 
8367 {
8369 }
8370 
8371 bool myless( const Cargo &a, const Cargo &b )
8372 {
8373  return a < b;
8374 }
8375 
8376 Cargo* Unit::GetCargo( const std::string &s, unsigned int &i )
8377 {
8378  const Unit *thus = this;
8379  if ( thus->GetCargo( s, i ) )
8380  return &GetCargo( i );
8381  return NULL;
8382 }
8383 
8384 const Cargo* Unit::GetCargo( const std::string &s, unsigned int &i ) const
8385 {
8386  static Hashtable< string, unsigned int, 2047 >index_cache_table;
8388  if (this == mpl) {
8389  unsigned int *ind = index_cache_table.Get( s );
8390  if (ind) {
8391  if ( *ind < pImage->cargo.size() ) {
8392  Cargo *guess = &pImage->cargo[*ind];
8393  if (guess->content == s) {
8394  i = *ind;
8395  return guess;
8396  }
8397  }
8398  }
8399  Cargo searchfor;
8400  searchfor.content = s;
8401  vector< Cargo >::iterator tmp = std::find( pImage->cargo.begin(), pImage->cargo.end(), searchfor );
8402  if ( tmp == pImage->cargo.end() )
8403  return NULL;
8404  if ( (*tmp).content == searchfor.content ) {
8405  i = ( tmp-pImage->cargo.begin() );
8406  if (this == mpl) {
8407  unsigned int *tmp = new unsigned int;
8408  *tmp = i;
8409  if ( index_cache_table.Get( s ) )
8410  index_cache_table.Delete( s );
8411  //memory leak--should not be reached though, ever
8412  index_cache_table.Put( s, tmp );
8413  }
8414  return &(*tmp);
8415  }
8416  return NULL;
8417  }
8418  Cargo searchfor;
8419  searchfor.content = s;
8420  vector< Cargo >::iterator tmp = ( std::find( pImage->cargo.begin(), pImage->cargo.end(), searchfor ) );
8421  if ( tmp == pImage->cargo.end() )
8422  return NULL;
8423  i = ( tmp-pImage->cargo.begin() );
8424  return &(*tmp);
8425 }
8426 
8427 unsigned int Unit::numCargo() const
8428 {
8429  return pImage->cargo.size();
8430 }
8431 
8432 std::string Unit::GetManifest( unsigned int i, Unit *scanningUnit, const Vector &oldspd ) const
8433 {
8435  string mangled = pImage->cargo[i].content;
8436  static float scramblingmanifest =
8437  XMLSupport::parse_float( vs_config->getVariable( "general", "PercentageSpeedChangeToFaultSearch", ".5" ) );
8438  {
8439  //Keep inside subblock, otherwice MSVC will throw an error while redefining 'i'
8440  bool last = true;
8441  for (string::iterator i = mangled.begin(); i != mangled.end(); ++i) {
8442  if (last)
8443  (*i) = toupper( *i );
8444  last = (*i == ' ' || *i == '_');
8445  }
8446  }
8447  if (CourseDeviation( oldspd, GetVelocity() ) > scramblingmanifest)
8448  for (string::iterator i = mangled.begin(); i != mangled.end(); ++i)
8449  (*i) += (rand()%3-1);
8450  return mangled;
8451 }
8452 
8453 bool Unit::SellCargo( unsigned int i, int quantity, float &creds, Cargo &carg, Unit *buyer )
8454 {
8455  if (i < 0 || i >= pImage->cargo.size() || !buyer->CanAddCargo( pImage->cargo[i] ) || Mass < pImage->cargo[i].mass)
8456  return false;
8457  carg = pImage->cargo[i];
8458  if (quantity > pImage->cargo[i].quantity)
8459  quantity = pImage->cargo[i].quantity;
8460  carg.price = buyer->PriceCargo( pImage->cargo[i].content );
8461  if ( !Network || _Universe->netLocked() )
8462  //Don't give cash back until server acknowledges purchase.
8463  creds += quantity*carg.price;
8464  if ( SERVER && !_Universe->netLocked() )
8465  VSServer->sendCredits( serial, creds );
8466  carg.quantity = quantity;
8467  buyer->AddCargo( carg );
8468 
8469  RemoveCargo( i, quantity );
8470  return true;
8471 }
8472 
8473 bool Unit::SellCargo( const std::string &s, int quantity, float &creds, Cargo &carg, Unit *buyer )
8474 {
8475  Cargo tmp;
8476  tmp.content = s;
8477  vector< Cargo >::iterator mycargo = std::find( pImage->cargo.begin(), pImage->cargo.end(), tmp );
8478  if ( mycargo == pImage->cargo.end() )
8479  return false;
8480  return SellCargo( mycargo-pImage->cargo.begin(), quantity, creds, carg, buyer );
8481 }
8482 
8483 bool Unit::BuyCargo( const Cargo &carg, float &creds )
8484 {
8485  if (!CanAddCargo( carg ) || creds < carg.quantity*carg.price)
8486  return false;
8487  AddCargo( carg );
8488  creds -= carg.quantity*carg.price;
8489  if ( Network && !_Universe->netLocked() )
8490  creds = 0;
8491  if ( SERVER && !_Universe->netLocked() )
8492  VSServer->sendCredits( serial, creds );
8493  return true;
8494 }
8495 
8496 bool Unit::BuyCargo( unsigned int i, unsigned int quantity, Unit *seller, float &creds )
8497 {
8498  Cargo soldcargo = seller->pImage->cargo[i];
8499  if (quantity > (unsigned int) soldcargo.quantity)
8500  quantity = soldcargo.quantity;
8501  if (quantity == 0)
8502  return false;
8503  soldcargo.quantity = quantity;
8504  if ( BuyCargo( soldcargo, creds ) ) {
8505  seller->RemoveCargo( i, quantity, false );
8506  return true;
8507  }
8508  return false;
8509 }
8510 
8511 bool Unit::BuyCargo( const std::string &cargo, unsigned int quantity, Unit *seller, float &creds )
8512 {
8513  unsigned int i;
8514  if ( seller->GetCargo( cargo, i ) )
8515  return BuyCargo( i, quantity, seller, creds );
8516  return false;
8517 }
8518 
8520 {
8521  unsigned int i;
8522  return GetUnitMasterPartList().GetCargo( input_buffer, i );
8523 }
8524 
8525 void Unit::ImportPartList( const std::string &category, float price, float pricedev, float quantity, float quantdev )
8526 {
8527  unsigned int numcarg = GetUnitMasterPartList().numCargo();
8528  float minprice = FLT_MAX;
8529  float maxprice = 0;
8530  for (unsigned int j = 0; j < numcarg; ++j)
8531  if (GetUnitMasterPartList().GetCargo( j ).category == category) {
8532  float price = GetUnitMasterPartList().GetCargo( j ).price;
8533  if (price < minprice)
8534  minprice = price;
8535  else if (price > maxprice)
8536  maxprice = price;
8537  }
8538  for (unsigned int i = 0; i < numcarg; ++i) {
8540  if (c.category == category) {
8541  static float aveweight =
8542  fabs( XMLSupport::parse_float( vs_config->getVariable( "cargo", "price_recenter_factor", "0" ) ) );
8543  c.quantity = float_to_int( quantity-quantdev );
8544  float baseprice = c.price;
8545  c.price *= price-pricedev;
8546 
8547  //stupid way
8548  c.quantity += float_to_int( (quantdev*2+1)*( (double) rand() )/( ( (double) RAND_MAX )+1 ) );
8549  c.price += pricedev*2*( (float) rand() )/RAND_MAX;
8550  c.price = fabs( c.price );
8551  c.price = ( c.price+(baseprice*aveweight) )/(aveweight+1);
8552  if (c.quantity <= 0) {
8553  c.quantity = 0;
8554  }
8555  //quantity more than zero
8556  else if (maxprice > minprice+.01) {
8557  float renormprice = (baseprice-minprice)/(maxprice-minprice);
8558  static float maxpricequantadj =
8559  XMLSupport::parse_float( vs_config->getVariable( "cargo", "max_price_quant_adj", "5" ) );
8560  static float minpricequantadj =
8561  XMLSupport::parse_float( vs_config->getVariable( "cargo", "min_price_quant_adj", "1" ) );
8562  static float powah = XMLSupport::parse_float( vs_config->getVariable( "cargo", "price_quant_adj_power", "1" ) );
8563  renormprice = pow( renormprice, powah );
8564  renormprice *= (maxpricequantadj-minpricequantadj);
8565  renormprice += 1;
8566  if (renormprice > .001) {
8567  c.quantity /= float_to_int( renormprice );
8568  if (c.quantity < 1)
8569  c.quantity = 1;
8570  }
8571  }
8572  static float minprice = XMLSupport::parse_float( vs_config->getVariable( "cargo", "min_cargo_price", "0.01" ) );
8573  if (c.price < minprice)
8574  c.price = minprice;
8575  c.quantity = abs( c.quantity );
8576  AddCargo( c, false );
8577  }
8578  }
8579 }
8580 
8581 std::string Unit::massSerializer( const XMLType &input, void *mythis )
8582 {
8583  Unit *un = (Unit*) mythis;
8584  float mass = un->Mass;
8585  static bool usemass = XMLSupport::parse_bool( vs_config->getVariable( "physics", "use_cargo_mass", "true" ) );
8586  for (unsigned int i = 0; i < un->pImage->cargo.size(); ++i)
8587  if (un->pImage->cargo[i].quantity > 0)
8588  if (usemass)
8589  mass -= un->pImage->cargo[i].mass*un->pImage->cargo[i].quantity;
8590  return XMLSupport::tostring( (float) mass );
8591 }
8592 
8593 std::string Unit::shieldSerializer( const XMLType &input, void *mythis )
8594 {
8595  Unit *un = (Unit*) mythis;
8596  switch (un->shield.number)
8597  {
8598  case 2:
8599  return tostring( un->shield.shield2fb.frontmax )+string( "\" back=\"" )+tostring( un->shield.shield2fb.backmax );
8600 
8601  case 8:
8602  return string( "\" frontrighttop=\"" )+tostring( un->shield.shield8.frontrighttop )+string( "\" backrighttop=\"" )
8603  +tostring( un->shield.shield8.backrighttop )+string( "\" frontlefttop=\"" )+tostring(
8604  un->shield.shield8.frontlefttop )
8605  +string( "\" backlefttop=\"" )+tostring( un->shield.shield8.backlefttop )+string( "\" frontrightbottom=\"" )
8606  +tostring(
8607  un->shield.shield8.frontrightbottom )+string( "\" backrightbottom=\"" )+tostring(
8608  un->shield.shield8.backrightbottom )
8609  +string( "\" frontleftbottom=\"" )+tostring( un->shield.shield8.frontleftbottom )+string( "\" backleftbottom=\"" )
8610  +tostring( un->shield.shield8.backleftbottom );
8611 
8612  case 4:
8613  default:
8614  return tostring( un->shield.shield4fbrl.frontmax )+string( "\" back=\"" )+tostring( un->shield.shield4fbrl.backmax )
8615  +string( "\" left=\"" )+tostring( un->shield.shield4fbrl.leftmax )+string( "\" right=\"" )+tostring(
8616  un->shield.shield4fbrl.rightmax );
8617  }
8618  return string( "" );
8619 }
8620 
8621 std::string Unit::mountSerializer( const XMLType &input, void *mythis )
8622 {
8623  Unit *un = (Unit*) mythis;
8624  int i = input.w.hardint;
8625  if (un->GetNumMounts() > i) {
8626  string result( lookupMountSize( un->mounts[i].size ) );
8627  if (un->mounts[i].status == Mount::INACTIVE || un->mounts[i].status == Mount::ACTIVE)
8628  result += string( "\" weapon=\"" )+(un->mounts[i].type->weapon_name);
8629  if (un->mounts[i].ammo != -1)
8630  result += string( "\" ammo=\"" )+XMLSupport::tostring( un->mounts[i].ammo );
8631  if (un->mounts[i].volume != -1)
8632  result += string( "\" volume=\"" )+XMLSupport::tostring( un->mounts[i].volume );
8633  result += string( "\" xyscale=\"" )+XMLSupport::tostring( un->mounts[i].xyscale )+string( "\" zscale=\"" )
8634  +XMLSupport::tostring( un->mounts[i].zscale );
8635  Matrix m;
8636  Transformation( un->mounts[i].GetMountOrientation(), un->mounts[i].GetMountLocation().Cast() ).to_matrix( m );
8637  result += string( "\" x=\"" )+tostring( (float) ( m.p.i/parse_float( input.str ) ) );
8638  result += string( "\" y=\"" )+tostring( (float) ( m.p.j/parse_float( input.str ) ) );
8639  result += string( "\" z=\"" )+tostring( (float) ( m.p.k/parse_float( input.str ) ) );
8640 
8641  result += string( "\" qi=\"" )+tostring( m.getQ().i );
8642  result += string( "\" qj=\"" )+tostring( m.getQ().j );
8643  result += string( "\" qk=\"" )+tostring( m.getQ().k );
8644 
8645  result += string( "\" ri=\"" )+tostring( m.getR().i );
8646  result += string( "\" rj=\"" )+tostring( m.getR().j );
8647  result += string( "\" rk=\"" )+tostring( m.getR().k );
8648  return result;
8649  } else {
8650  return string( "" );
8651  }
8652 }
8653 
8654 std::string Unit::subunitSerializer( const XMLType &input, void *mythis )
8655 {
8656  Unit *un = (Unit*) mythis;
8657  int index = input.w.hardint;
8658  Unit *su;
8659  int i = 0;
8660  for (un_iter ui = un->getSubUnits(); (su = *ui); ++ui, ++i) {
8661  if (i == index) {
8662  if (su->pImage->unitwriter)
8663  return su->pImage->unitwriter->getName();
8664  return su->name;
8665  }
8666  }
8667  return string( "destroyed_blank" );
8668 }
8669 
8670 void Unit::setUnitRole( const std::string &s )
8671 {
8672  unitRole( ROLES::getRole( s ) );
8673 }
8674 
8675 void Unit::setAttackPreference( const std::string &s )
8676 {
8677  attackPreference( ROLES::getRole( s ) );
8678 }
8679 
8680 const std::string& Unit::getUnitRole() const
8681 {
8682  return ROLES::getRole( unitRole() );
8683 }
8684 
8685 const std::string& Unit::getAttackPreference() const
8686 {
8687  return ROLES::getRole( attackPreference() );
8688 }
8689 
8690 //legacy function for python
8691 void Unit::setCombatRole( const std::string &s )
8692 {
8693  unitRole( ROLES::getRole( s ) );
8694  attackPreference( ROLES::getRole( s ) );
8695 }
8696 
8697 //legacy function for python
8698 const std::string& Unit::getCombatRole() const
8699 {
8700  static unsigned char inert = ROLES::getRole( "INERT" );
8701  unsigned char retA = unitRole();
8702  unsigned char retB = attackPreference();
8703 
8704  //often missions used this to render items either uninteresting or not attacking...so want to prioritize that behavior
8705  if (retA == inert || retB == inert) {
8706  static const std::string INERT("INERT");
8707  return INERT;
8708  }
8709 
8710  return ROLES::getRole( retA );
8711 }
8712 
8714 {
8715  Unit *un = this;
8716  std::sort( un->pImage->cargo.begin(), un->pImage->cargo.end() );
8717  for (unsigned int i = 0; i+1 < un->pImage->cargo.size(); ++i)
8718  if (un->pImage->cargo[i].content == un->pImage->cargo[i+1].content) {
8719  float tmpmass = un->pImage->cargo[i].quantity*un->pImage->cargo[i].mass
8720  +un->pImage->cargo[i+1].quantity*un->pImage->cargo[i+1].mass;
8721  float tmpvolume = un->pImage->cargo[i].quantity*un->pImage->cargo[i].volume
8722  +un->pImage->cargo[i+1].quantity*un->pImage->cargo[i+1].volume;
8723  un->pImage->cargo[i].quantity += un->pImage->cargo[i+1].quantity;
8724  if (un->pImage->cargo[i].quantity) {
8725  tmpmass /= un->pImage->cargo[i].quantity;
8726  tmpvolume /= un->pImage->cargo[i].quantity;
8727  }
8728  un->pImage->cargo[i].volume = tmpvolume;
8729  un->pImage->cargo[i].mission = (un->pImage->cargo[i].mission || un->pImage->cargo[i+1].mission);
8730  un->pImage->cargo[i].mass = tmpmass;
8731  //group up similar ones
8732  un->pImage->cargo.erase( un->pImage->cargo.begin()+(i+1) );
8733  i--;
8734  }
8735 }
8736 
8737 using XMLSupport::tostring;
8738 using std::string;
8739 
8740 std::string CargoToString( const Cargo &cargo )
8741 {
8742  string missioncargo;
8743  if (cargo.mission)
8744  missioncargo = string( "\" missioncargo=\"" )+XMLSupport::tostring( cargo.mission );
8745  return string( "\t\t\t<Cargo mass=\"" )+XMLSupport::tostring( (float) cargo.mass )+string( "\" price=\"" )
8746  +XMLSupport::tostring( (float) cargo.price )+string( "\" volume=\"" )+XMLSupport::tostring( (float) cargo.volume )
8747  +string(
8748  "\" quantity=\"" )+XMLSupport::tostring( (int) cargo.quantity )+string( "\" file=\"" )+cargo.GetContent()
8749  +missioncargo
8750  +string( "\"/>\n" );
8751 }
8752 
8753 std::string Unit::cargoSerializer( const XMLType &input, void *mythis )
8754 {
8755  Unit *un = (Unit*) mythis;
8756  if (un->pImage->cargo.size() == 0)
8757  return string( "0" );
8758  un->SortCargo();
8759  string retval( "" );
8760  if ( !( un->pImage->cargo.empty() ) ) {
8761  retval = un->pImage->cargo[0].GetCategory()+string( "\">\n" )+CargoToString( un->pImage->cargo[0] );
8762  for (unsigned int kk = 1; kk < un->pImage->cargo.size(); ++kk) {
8763  if (un->pImage->cargo[kk].category != un->pImage->cargo[kk-1].category)
8764  retval += string( "\t\t</Category>\n\t\t<Category file=\"" )+un->pImage->cargo[kk].GetCategory()+string(
8765  "\">\n" );
8766  retval += CargoToString( un->pImage->cargo[kk] );
8767  }
8768  retval += string( "\t\t</Category>\n\t\t<Category file=\"nothing" );
8769  } else {
8770  retval = string( "nothing" );
8771  }
8772  return retval;
8773 }
8774 
8775 float Unit::CourseDeviation( const Vector &OriginalCourse, const Vector &FinalCourse ) const
8776 {
8777  if (ViewComputerData().max_ab_speed() > .001)
8778  return ( OriginalCourse-(FinalCourse) ).Magnitude()/ViewComputerData().max_ab_speed();
8779  else
8780  return (FinalCourse-OriginalCourse).Magnitude();
8781 }
8782 
8783 /*
8784  **************************************************************************************
8785  *** STAR SYSTEM JUMP STUFF **
8786  **************************************************************************************
8787  */
8788 
8790 {
8791  if ( getStarSystem()->RemoveUnit( this ) ) {
8792  this->RemoveFromSystem();
8793  this->Target( NULL );
8794  Current->AddUnit( this );
8795 
8796  Cockpit *an_active_cockpit = _Universe->isPlayerStarship( this );
8797  if (an_active_cockpit != NULL) {
8798  an_active_cockpit->activeStarSystem = Current;
8799  an_active_cockpit->visitSystem( Current->getFileName() );
8800  }
8801  activeStarSystem = Current;
8802  return true;
8803  } else {
8804  VSFileSystem::vs_fprintf( stderr, "Fatal Error: cannot remove starship from critical system" );
8805  }
8806  return false;
8807 }
8808 
8809 /*
8810  **************************************************************************************
8811  *** UNIT_REPAIR STUFF **
8812  **************************************************************************************
8813  */
8814 extern float rand01();
8815 bool isWeapon( std::string name )
8816 {
8817  if (name.find( "Weapon" ) != std::string::npos)
8818  return true;
8819  if (name.find( "SubUnit" ) != std::string::npos)
8820  return true;
8821  if (name.find( "Ammunition" ) != std::string::npos)
8822  return true;
8823  return false;
8824 }
8825 
8826 #define REPAIRINTEGRATED( functionality, max_functionality ) \
8827  do { \
8828  if (functionality < max_functionality) \
8829  { \
8830  (functionality) += ammt_repair; \
8831  if ( (functionality) > (max_functionality) ) \
8832  (functionality) = (max_functionality); \
8833  } \
8834  } \
8835  while (0)
8836 
8838 {
8839  static float repairtime = XMLSupport::parse_float( vs_config->getVariable( "physics", "RepairDroidTime", "180" ) );
8840  static float checktime = XMLSupport::parse_float( vs_config->getVariable( "physics", "RepairDroidCheckTime", "5" ) );
8841  if ( (repairtime <= 0) || (checktime <= 0) ) return;
8842  if (pImage->repair_droid > 0) {
8843  if ( pImage->next_repair_time == -FLT_MAX || pImage->next_repair_time <= UniverseUtil::GetGameTime() ) {
8844  unsigned int numcargo = numCargo();
8845  if (numcargo > 0) {
8846  if ( pImage->next_repair_cargo >= numCargo() )
8847  pImage->next_repair_cargo = 0;
8848  Cargo *carg = &GetCargo( pImage->next_repair_cargo );
8849  float percentoperational = 1;
8850  if ( carg->GetCategory().find( "upgrades/" ) == 0
8851  && carg->GetCategory().find( DamagedCategory ) != 0
8852  && carg->GetContent().find( "add_" ) != 0
8853  && carg->GetContent().find( "mult_" ) != 0
8854  && ( ( percentoperational =
8855  UnitUtil::PercentOperational( this, carg->content, carg->category, true ) ) < 1.f ) ) {
8856  if (pImage->next_repair_time == -FLT_MAX) {
8857  pImage->next_repair_time = UniverseUtil::GetGameTime()+repairtime*(1-percentoperational)/pImage->repair_droid;
8858  } else {
8859  //ACtually fix the cargo here
8860  static int upfac = FactionUtil::GetUpgradeFaction();
8861  const Unit *up = getUnitFromUpgradeName( carg->content, upfac );
8862  static std::string loadfailed( "LOAD_FAILED" );
8863  if (up->name == loadfailed) {
8864  printf( "Bug: Load failed cargo encountered: report to hellcatv@hotmail.com\n" );
8865  } else {
8866  double percentage = 0;
8867  //don't want to repair these things
8868  if (up->SubUnits.empty() && up->GetNumMounts() == 0) {
8869  this->Upgrade( up, 0, 0, 0, true, percentage, makeTemplateUpgrade( this->name,
8870  this->faction ), false,
8871  false );
8872  if (percentage == 0) {
8874  stderr, "Failed repair for unit %s, cargo item %d: %s (%s) - please report error\n",
8875  name.get().c_str(), pImage->next_repair_cargo, carg->GetContent().c_str(),
8876  carg->GetCategory().c_str() );
8877  }
8878  }
8879  }
8880  pImage->next_repair_time = -FLT_MAX;
8881  ++(pImage->next_repair_cargo);
8882  }
8883  } else {
8884  ++(pImage->next_repair_cargo);
8885  }
8886  }
8887  }
8888  float ammt_repair = SIMULATION_ATOM/repairtime*pImage->repair_droid;
8889  REPAIRINTEGRATED( pImage->LifeSupportFunctionality, pImage->LifeSupportFunctionalityMax );
8890  REPAIRINTEGRATED( pImage->fireControlFunctionality, pImage->fireControlFunctionalityMax );
8891  REPAIRINTEGRATED( pImage->SPECDriveFunctionality, pImage->SPECDriveFunctionalityMax );
8892  REPAIRINTEGRATED( pImage->CommFunctionality, pImage->CommFunctionalityMax );
8893  unsigned int numg = (1+UnitImages< void >::NUMGAUGES+MAXVDUS);
8894  unsigned int which = vsrandom.genrand_int31()%numg;
8895  static float hud_repair_quantity = XMLSupport::parse_float( vs_config->getVariable( "physics", "hud_repair_unit", ".25" ) );
8896  //total damage
8897  if (pImage->cockpit_damage[which] < pImage->cockpit_damage[which+numg]) {
8898  pImage->cockpit_damage[which] += hud_repair_quantity;
8899  if (pImage->cockpit_damage[which] > pImage->cockpit_damage[which+numg])
8900  //total damage
8901  pImage->cockpit_damage[which] = pImage->cockpit_damage[which+numg];
8902  }
8903  if ( mounts.size() ) {
8904  static float mount_repair_quantity =
8905  XMLSupport::parse_float( vs_config->getVariable( "physics", "mount_repair_unit", ".25" ) );
8906  unsigned int i = vsrandom.genrand_int31()%mounts.size();
8907  if (mounts[i].functionality < mounts[i].maxfunctionality) {
8908  mounts[i].functionality += mount_repair_quantity;
8909  if (mounts[i].functionality > mounts[i].maxfunctionality)
8910  mounts[i].functionality = mounts[i].maxfunctionality;
8911  }
8912  }
8913  }
8914 }
8915 
8916 #undef REPAIRINTEGRATED
8917 
8918 bool Unit::isTractorable( enum tractorHow how ) const
8919 {
8920  if (how != tractorImmune)
8921  return (getTractorability()&how) == how;
8922 
8923  else
8924  return getTractorability() == tractorImmune;
8925 }
8926 
8928 {
8929  tractorability_flags = how;
8930 }
8931 
8933 {
8934  static bool tractorability_mask_init = false;
8935  static unsigned char tractorability_mask;
8936  if (!tractorability_mask_init) {
8937  std::string stractorability_mask = vs_config->getVariable( "physics", "PlayerTractorabilityMask", "p" );
8938  if ( !stractorability_mask.empty() ) {
8939  tractorability_mask = tractorImmune;
8940  if (stractorability_mask.find_first_of( "pP" ) != string::npos)
8941  tractorability_mask |= tractorPush;
8942  if (stractorability_mask.find_first_of( "iI" ) != string::npos)
8943  tractorability_mask |= tractorIn;
8944  } else {
8945  tractorability_mask = tractorPush;
8946  }
8947  tractorability_mask_init = true;
8948  }
8949  unsigned char tflags;
8950  if (_Universe->isPlayerStarship( this ) != NULL)
8951  tflags = tractorability_flags&tractorability_mask;
8952 
8953  else
8954  tflags = tractorability_flags;
8955  return (Unit::tractorHow) (tflags);
8956 }
8957 
8959 {
8960  //Request ASAP physics
8961  if ( getStarSystem() )
8962  getStarSystem()->RequestPhysics( this, cur_sim_queue_slot );
8963 }
8964 
8965 void Unit::applyTechniqueOverrides(const std::map<std::string, std::string> &overrides)
8966 {
8967  // No-op
8968  // FIXME ?
8969 }
8970 
8971 std::map< string, Unit * > MeshAnimation::Units;
8972 
8974  animatedMesh(true),
8975  activeAnimation(0),
8976  timeperframe(3.0),
8977  done(true),
8978  activeMesh(0),
8979  nextactiveMesh(1),
8980  infiniteLoop(true),
8981  loopCount(0),
8982  unitDst(_unitDst),
8983  curtime(0.0)
8984 
8985 {}
8986 
8987 //helper func for Init
8988 string toLowerCase( string in )
8989 {
8990  string out;
8991  for(unsigned int i = 0; i < in.length(); i++)
8992  {
8993  switch(in[i])
8994  {
8995  case 'A': out +='a'; break;
8996  case 'B': out +='b'; break;
8997  case 'C': out +='c'; break;
8998  case 'D': out +='d'; break;
8999  case 'E': out +='e'; break;
9000  case 'F': out +='f'; break;
9001  case 'G': out +='g'; break;
9002  case 'H': out +='h'; break;
9003  case 'I': out +='i'; break;
9004  case 'J': out +='j'; break;
9005  case 'K': out +='k'; break;
9006  case 'L': out +='l'; break;
9007  case 'M': out +='m'; break;
9008  case 'N': out +='n'; break;
9009  case 'O': out +='o'; break;
9010  case 'P': out +='p'; break;
9011  case 'Q': out +='q'; break;
9012  case 'R': out +='r'; break;
9013  case 'S': out +='s'; break;
9014  case 'T': out +='t'; break;
9015  case 'U': out +='u'; break;
9016  case 'V': out +='v'; break;
9017  case 'W': out +='w'; break;
9018  case 'X': out +='x'; break;
9019  case 'Y': out +='y'; break;
9020  case 'Z': out +='z'; break;
9021  default:
9022  out += in[i];
9023  }
9024  }
9025  return out;
9026 }
9027 
9028 unsigned int MeshAnimation::unitCount = 0;
9029 
9030 bool MeshAnimation::Init(const char *filename, int faction,
9031  Flightgroup *flightgrp, const char *animationExt)
9032 {
9033  string fnam(filename);
9034  string::size_type pos = fnam.find('.');
9035  string anifilename = fnam.substr(0, pos);
9036 
9037  if(animationExt)
9038  anifilename += string("_") + string(animationExt);
9039 
9040  std::vector< Mesh* > *meshes = new vector<Mesh *>();
9041  int i = 1;
9042  char count[30] = "1";
9043  string dir = anifilename;
9044  while(true)
9045  {
9046  sprintf( count, "%d", i );
9047  string unit_name = toLowerCase(anifilename) + "_";
9048  if(i < 10)
9049  unit_name += "0";
9050  if(i < 100)
9051  unit_name += "0";
9052  if(i < 1000)
9053  unit_name += "0";
9054  if(i < 10000)
9055  unit_name += "0";
9056  if(i < 100000)
9057  unit_name += "0";
9058 
9059  unit_name += count;
9060  string path = dir + "/" + unit_name + ".bfxm";
9062  {
9063  Mesh *m = Mesh::LoadMesh( path.c_str(), Vector(1,1,1), faction, flightgrp );
9064  meshes->push_back( m );
9065  #ifdef DEBUG_MESH_ANI
9066  cerr << "Animated Mesh: " << path << " loaded - with: " << m->getVertexList()->GetNumVertices() << " vertices." << endl;
9067  #endif
9068  }
9069  else
9070  break;
9071  i++;
9072  }
9073 
9074 
9075  if( meshes->size() != 0 )
9076  {
9077  //FIXME: an animation is created only for the first submesh
9078  string animationName;
9079  sprintf( count, "%lu", meshes->size() );
9080  if(!animationExt)
9081  animationName = string(count); //if there is no extension given, the animations are called by their load order, 1, 2 ,3 ....10..
9082  else
9083  animationName = animationExt;
9084  addAnimation(meshes, animationName.c_str());
9085 
9086  int numFrames = meshes->size();
9088  sprintf( count, "%u", unitCount );
9089  uniqueUnitName = unitDst->name + string(count);
9091  cerr << "Animation data loaded for unit: " << string(filename) << ", named " << uniqueUnitName << " - with: " << numFrames << " frames." << endl;
9092  return true;
9093  } else {
9094  delete meshes;
9095  return false;
9096  }
9097 }
9098 
9100 {
9101 #ifdef DEBUG_MESH_ANI
9102  std::cerr << "Starting animation step of Unit: " << uniqueUnitName << std::endl;
9103 #endif
9104  if((!this->isContinuousLoop())&&(loopCount==0))
9105  return;
9106 
9107  int numvold = 0;
9108  int numvertices = 0;
9109  //copy reference to data
9111 
9112 #ifdef DEBUG_MESH_ANI
9113  std::cerr << "vertices changed from: " << numvold << " to: " << numvertices << std::endl;
9114 #endif
9115 
9116  unitDst->Draw();
9117 
9118 #ifdef DEBUG_MESH_ANI
9119  std::cerr << "Drawed mesh: " << uniqueUnitName << std::endl;
9120 #endif
9121 
9123  nextactiveMesh++;
9124  if(nextactiveMesh >= vecAnimations.at(activeAnimation)->size())
9125  nextactiveMesh = 0;
9126 
9127  if(loopCount > 0)
9128  loopCount--;
9129 #ifdef DEBUG_MESH_ANI
9130  std::cerr << "Ending animation step of Unit: " << uniqueUnitName << std::endl;
9131 #endif
9132 }
9133 
9135 {
9136  std::map< string, Unit * >::iterator pos;
9137  for(pos = Units.begin(); pos != Units.end(); ++pos)
9138  {
9139  pos->second->pMeshAnimation->curtime += GetElapsedTime();
9140  if (pos->second->pMeshAnimation->curtime >= pos->second->pMeshAnimation->timePerFrame()) {
9141  pos->second->pMeshAnimation->AnimationStep();
9142  pos->second->pMeshAnimation->curtime = 0.0;
9143  }
9144  }
9145 }
9146 
9147 void MeshAnimation::addAnimation( std::vector<Mesh *> *meshes, const char* name )
9148 {
9149  if( (meshes->size() > 0) && animatedMesh ) {
9150  vecAnimations.push_back( meshes );
9151  vecAnimationNames.push_back(string(name));
9152  }
9153 }
9154 
9155 void MeshAnimation::StartAnimation( unsigned int how_many_times, int numAnimation )
9156 {
9157  bool infiniteLoop;
9158  if(!how_many_times)
9159  infiniteLoop = true;
9160  else
9161  infiniteLoop = false;
9162 
9163  if(animationRuns())
9164  StopAnimation();
9165 
9166  done = false;
9167 }
9168 
9170 {
9171  done = true;
9172 }
9173 
9174 string MeshAnimation::getAnimationName(unsigned int animationNumber) const
9175 {
9176  return vecAnimationNames.at(animationNumber);
9177 }
9178 
9179 unsigned int MeshAnimation::getAnimationNumber(const char *name) const
9180 {
9181  string strname(name);
9182  for(unsigned int i=0;i<vecAnimationNames.size();i++)
9183  if(strname == vecAnimationNames[i])
9184  return i;
9185 
9186  return 0; //NOT FOUND!
9187 }
9188 
9189 void MeshAnimation::ChangeAnimation( const char *name )
9190 {
9191  unsigned int AnimNumber = getAnimationNumber(name);
9192  if( (AnimNumber < numAnimations()) && isAnimatedMesh() )
9193  activeAnimation = AnimNumber;
9194 }
9195 
9196 void MeshAnimation::ChangeAnimation( unsigned int AnimNumber )
9197 {
9198  if( (AnimNumber < numAnimations()) && isAnimatedMesh() )
9199  activeAnimation = AnimNumber;
9200 }
9201 
9203 {
9204  return animatedMesh;
9205 }
9206 
9208 {
9209  return 1/timeperframe;
9210 }
9211 
9213 {
9214  return timeperframe;
9215 }
9216 
9218 {
9219  return vecAnimations.size();
9220 }
9221 
9223 {
9224  animatedMesh = on;
9225 }
9226 
9228 {
9229  return infiniteLoop;
9230 }
9231 
9233 {
9234  timeperframe = speed;
9235 }
9236 
9238 {
9239  StopAnimation();
9240 
9241  for (unsigned int i = 0; i < vecAnimations.size(); i++) {
9242  for(unsigned int j = 0; j < vecAnimations[i]->size(); j++) {
9243  delete vecAnimations[i]->at(j);
9244  }
9245  delete vecAnimations[i];
9246  vecAnimations[i]->clear();
9247  }
9248  vecAnimations.clear();
9249  vecAnimationNames.clear();
9250 
9251  Units.erase(uniqueUnitName);
9252 }
9253 
9255 {
9256  return !done;
9257 }
9258 
9260 {
9262  {
9264  return Brand::SPHERE;
9266  return Brand::BUBBLE;
9267  case Capability::IFF_PLANE:
9268  return Brand::PLANE;
9269  default:
9270  assert(false);
9271  return Brand::SPHERE;
9272  }
9273 }
9274 
9276 {
9277  // Backwardscompatibility
9278  if (capability == 0)
9279  return false;
9280  else if ((capability == 1) || (capability == 2))
9281  return true;
9282 
9283  return (capability & Capability::IFF_FRIEND_FOE);
9284 }
9285 
9287 {
9288  // Backwardscompatibility
9289  if ((capability == 0) || (capability == 1))
9290  return false;
9291  else if (capability == 2)
9292  return true;
9293 
9294  return (capability & Capability::IFF_OBJECT_RECOGNITION);
9295 }
9296 
9298 {
9299  return (capability & Capability::IFF_THREAT_ASSESSMENT);
9300 }