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
fire.cpp
Go to the documentation of this file.
1 #include "fire.h"
2 #include "flybywire.h"
3 #include "navigation.h"
4 #include "cmd/planet_generic.h"
5 #include "config_xml.h"
6 #include "vs_globals.h"
7 #include "cmd/unit_util.h"
9 #include "cmd/role_bitmask.h"
10 #include "cmd/ai/communication.h"
11 #include "universe_util.h"
12 #include <algorithm>
13 #include "cmd/unit_find.h"
14 #include "vs_random.h"
15 #include "lin_time.h" //DEBUG ONLY
16 #include "cmd/pilot.h"
17 
18 extern int numprocessed;
19 extern double targetpick;
20 
21 static bool NoDockWithClear()
22 {
23  static bool nodockwithclear = XMLSupport::parse_bool( vs_config->getVariable( "physics", "dock_with_clear_planets", "true" ) );
24  return nodockwithclear;
25 }
26 
27 VSRandom targrand( time( NULL ) );
28 
30 {
31  if (targ) {
32  Unit *un;
34  (un = *i) != NULL;
35  ++i)
36  if (un->isUnit() == PLANETPTR) {
37  if ( ( targ->Position()-un->Position() ).Magnitude() < targ->rSize()*.5 )
38  if ( !( ( (Planet*) un )->isAtmospheric() ) )
39  return un;
40  }
41  }
42  return NULL;
43 }
44 
45 bool RequestClearence( Unit *parent, Unit *targ, unsigned char sex )
46 {
47  if ( !targ->DockingPortLocations().size() )
48  return false;
49  if (targ->isUnit() == PLANETPTR) {
50  if ( ( (Planet*) targ )->isAtmospheric() && NoDockWithClear() ) {
51  targ = getAtmospheric( targ );
52  if (!targ)
53  return false;
54  parent->Target( targ );
55  }
56  }
57  CommunicationMessage c( parent, targ, NULL, sex );
58  c.SetCurrentState( c.fsm->GetRequestLandNode(), NULL, sex );
59  Order *o = targ->getAIState();
60  if (o)
61  o->Communicate( c );
62  return true;
63 }
64 
65 using Orders::FireAt;
66 bool FireAt::PursueTarget( Unit *un, bool leader )
67 {
68  if (leader)
69  return true;
70  if ( un == parent->Target() )
71  return rand() < .9*RAND_MAX;
72  if (parent->getRelation( un ) < 0)
73  return rand() < .2*RAND_MAX;
74  return false;
75 }
76 
77 bool CanFaceTarget( Unit *su, Unit *targ, const Matrix &matrix )
78 {
79  return true;
80 
81  float limitmin = su->Limits().limitmin;
82  if (limitmin > -.99) {
83  QVector pos = ( targ->Position()-su->Position() ).Normalize();
84  QVector pnorm = pos.Cast();
85  Vector structurelimits = su->Limits().structurelimits;
86  Vector worldlimit = TransformNormal( matrix, structurelimits );
87  if (pnorm.Dot( worldlimit ) < limitmin)
88  return false;
89  }
90  Unit *ssu;
91  for (un_iter i = su->getSubUnits();
92  (ssu = *i) != NULL;
93  ++i)
94  if ( !CanFaceTarget( ssu, targ, su->cumulative_transformation_matrix ) )
95  return false;
96  return true;
97 }
98 
99 void FireAt::ReInit( float aggressivitylevel )
100 {
101  static float missileprob = XMLSupport::parse_float( vs_config->getVariable( "AI", "Firing", "MissileProbability", ".01" ) );
102  static float mintimetoswitch =
103  XMLSupport::parse_float( vs_config->getVariable( "AI", "Targetting", "MinTimeToSwitchTargets", "3" ) );
105  missileprobability = missileprob;
106  delay = 0;
107  agg = aggressivitylevel;
108  distance = 1;
109  //JS --- spreading target switch times
110  lastchangedtarg = 0.0-targrand.uniformInc( 0, 1 )*mintimetoswitch;
111  had_target = false;
112 }
113 
114 FireAt::FireAt( float aggressivitylevel ) : CommunicatingAI( WEAPON, STARGET )
115 {
116  ReInit( aggressivitylevel );
117 }
118 
120 {
121  static float aggr = XMLSupport::parse_float( vs_config->getVariable( "AI", "Firing", "Aggressivity", "15" ) );
122  ReInit( aggr );
123 }
124 
126 //temporary way of choosing
128 {
129  Unit *t;
130  float range;
131  float relation;
132  TargetAndRange( Unit *tt, float r, float rel )
133  {
134  t = tt;
135  range = r;
136  this->relation = rel;
137  }
138 };
139 
141 {
143  float gunrange;
144  RangeSortedTurrets( Unit *t, float r )
145  {
146  tur = t;
147  gunrange = r;
148  }
149  bool operator<( const RangeSortedTurrets &o ) const
150  {
151  return gunrange < o.gunrange;
152  }
153 };
154 
155 struct TurretBin
156 {
157  float maxrange;
158  vector< RangeSortedTurrets >turret;
159  vector< TargetAndRange > listOfTargets[2]; //we have the over (and eq) 16 crowd and the under 16
161  {
162  maxrange = 0;
163  }
164  bool operator<( const TurretBin &o ) const
165  {
166  return maxrange > o.maxrange;
167  }
168  void AssignTargets( const TargetAndRange &finalChoice, const Matrix &pos )
169  {
170  //go through modularly assigning as you go;
171  int count = 0;
172  const unsigned int lotsize[2] = {listOfTargets[0].size(), listOfTargets[1].size()};
173  for (vector< RangeSortedTurrets >::iterator uniter = turret.begin(); uniter != turret.end(); ++uniter) {
174  bool foundfinal = false;
175  uniter->tur->Target( NULL );
176  uniter->tur->TargetTurret( NULL );
177  if (finalChoice.t) {
178  if (finalChoice.range < uniter->gunrange
179  && ROLES::getPriority( uniter->tur->attackPreference() )[finalChoice.t->unitRole()] < 31) {
180  if ( CanFaceTarget( uniter->tur, finalChoice.t, pos ) ) {
181  uniter->tur->Target( finalChoice.t );
182  uniter->tur->TargetTurret( finalChoice.t );
183  foundfinal = true;
184  }
185  }
186  }
187  if (!foundfinal) {
188  for (unsigned int f = 0; f < 2 && !foundfinal; f++) {
189  for (unsigned int i = 0; i < lotsize[f]; i++) {
190  const int index = (count+i)%lotsize[f];
191  if (listOfTargets[f][index].range < uniter->gunrange) {
192  if ( CanFaceTarget( uniter->tur, finalChoice.t, pos ) ) {
193  uniter->tur->Target( listOfTargets[f][index].t );
194  uniter->tur->TargetTurret( listOfTargets[f][index].t );
195  count++;
196  foundfinal = true;
197  break;
198  }
199  }
200  }
201  }
202  }
203  }
204  }
205 };
206 
207 void AssignTBin( Unit *su, vector< TurretBin > &tbin )
208 {
209  unsigned int bnum = 0;
210  for (; bnum < tbin.size(); bnum++)
211  if ( su->attackPreference() == tbin[bnum].turret[0].tur->attackPreference() )
212  break;
213  if ( bnum >= tbin.size() )
214  tbin.push_back( TurretBin() );
215  float gspeed, grange, mrange;
216  grange = FLT_MAX;
217  su->getAverageGunSpeed( gspeed, grange, mrange );
218  {
219  float ggspeed, ggrange, mmrange;
220  Unit *ssu;
221  for (un_iter i = su->getSubUnits(); (ssu = *i) != NULL; ++i) {
222  ssu->getAverageGunSpeed( ggspeed, ggrange, mmrange );
223  if (ggspeed > gspeed) gspeed = ggspeed;
224  if (ggrange > grange) grange = ggrange;
225  if (mmrange > mrange) mrange = mmrange;
226  }
227  }
228  if (tbin[bnum].maxrange < grange)
229  tbin[bnum].maxrange = grange;
230  tbin[bnum].turret.push_back( RangeSortedTurrets( su, grange ) );
231 }
232 
233 float Priority( Unit *me, Unit *targ, float gunrange, float rangetotarget, float relationship, char *rolepriority )
234 {
235  if (relationship >= 0)
236  return -1;
237  if (targ->GetHull() < 0)
238  return -1;
239  *rolepriority = ROLES::getPriority( me->attackPreference() )[targ->unitRole()]; //number btw 0 and 31 higher better
240  char invrolepriority = 31-*rolepriority;
241  if (invrolepriority <= 0)
242  return -1;
243  if (rangetotarget < 1 && rangetotarget > -1000)
244  rangetotarget = 1;
245  else
246  rangetotarget = fabs( rangetotarget );
247  if (rangetotarget < .5*gunrange)
248  rangetotarget = .5*gunrange;
249  if (gunrange <= 0) {
250  static float mountless_gunrange =
251  XMLSupport::parse_float( vs_config->getVariable( "AI", "Targetting", "MountlessGunRange", "300000000" ) );
252  gunrange = mountless_gunrange;
253  } //probably a mountless capship. 50000 is chosen arbitrarily
254  float inertial_priority = 0;
255  {
256  static float mass_inertial_priority_cutoff =
257  XMLSupport::parse_float( vs_config->getVariable( "AI", "Targetting", "MassInertialPriorityCutoff", "5000" ) );
258  if (me->GetMass() > mass_inertial_priority_cutoff) {
259  static float mass_inertial_priority_scale =
260  XMLSupport::parse_float( vs_config->getVariable( "AI", "Targetting", "MassInertialPriorityScale", ".0000001" ) );
261  Vector normv( me->GetVelocity() );
262  float Speed = me->GetVelocity().Magnitude();
263  normv *= Speed ? 1.0f/Speed : 1.0f;
264  Vector ourToThem = targ->Position()-me->Position();
265  ourToThem.Normalize();
266  inertial_priority = mass_inertial_priority_scale*( .5+.5*( normv.Dot( ourToThem ) ) )*me->GetMass()*Speed;
267  }
268  }
269  static float threat_weight = XMLSupport::parse_float( vs_config->getVariable( "AI", "Targetting", "ThreatWeight", ".5" ) );
270  float threat_priority = (me->Threat() == targ) ? threat_weight : 0;
271  threat_priority += (targ->Target() == me) ? threat_weight : 0;
272  float role_priority01 = ( (float) *rolepriority )/31.;
273  float range_priority01 = .5*gunrange/rangetotarget; //number between 0 and 1 for most ships 1 is best
274  return range_priority01*role_priority01+inertial_priority+threat_priority;
275 }
276 
277 float Priority( Unit *me, Unit *targ, float gunrange, float rangetotarget, float relationship )
278 {
279  char rolepriority = 0;
280  return Priority( me, targ, gunrange, rangetotarget, relationship, &rolepriority );
281 }
282 
283 template < class T, size_t n >
285 {
286 public:
287  T vec[n];
288  size_t size()
289  {
290  return n;
291  }
292  T&operator[]( size_t index )
293  {
294  return vec[index];
295  }
296  const T& operator[]( size_t index ) const
297  {
298  return vec[index];
299  }
300 };
301 
302 template < size_t numTuple >
304 {
305  Unit *parent;
306  Unit *parentparent;
307  vector< TurretBin > *tbin;
308  StaticTuple< float, numTuple >maxinnerrangeless;
309  StaticTuple< float, numTuple >maxinnerrangemore;
310  float priority;
311  char rolepriority;
312  char maxrolepriority;
313  bool reachedMore;
314  bool reachedLess;
315  FireAt *fireat;
316  float gunrange;
317  int numtargets;
318  int maxtargets;
319 public:
322  void init( FireAt *fireat,
323  Unit *un,
324  float gunrange,
325  vector< TurretBin > *tbin,
326  const StaticTuple< float, numTuple > &innermaxrange,
327  char maxrolepriority,
328  int maxtargets )
329  {
330  this->fireat = fireat;
331  this->tbin = tbin;
332  this->parent = un;
333  this->parentparent = un->owner ? UniverseUtil::getUnitByPtr( un->owner, un, false ) : 0;
334  mytarg = NULL;
335  double currad = 0;
336  if ( !is_null( un->location[Unit::UNIT_ONLY] ) )
337  currad = un->location[Unit::UNIT_ONLY]->getKey();
338  for (size_t i = 0; i < numTuple; ++i) {
339  double tmpless = currad-innermaxrange[i];
340  double tmpmore = currad+innermaxrange[i];
341  this->maxinnerrangeless[i] = tmpless;
342  this->maxinnerrangemore[i] = tmpmore;
343  }
344  this->maxrolepriority = maxrolepriority; //max priority that will allow gun range to be ok
345  reachedMore = false;
346  reachedLess = false;
347  this->priority = -1;
348  this->rolepriority = 31;
349  this->gunrange = gunrange;
350  this->numtargets = 0;
351  this->maxtargets = maxtargets;
352  }
353  bool acquire( Unit *un, float distance )
354  {
355  double unkey = un->location[Unit::UNIT_ONLY]->getKey();
356  bool lesscheck = unkey < maxinnerrangeless[0];
357  bool morecheck = unkey > maxinnerrangemore[0];
358  if (reachedMore == false || reachedLess == false) {
359  if (lesscheck || morecheck) {
360  if (lesscheck)
361  reachedLess = true;
362  if (morecheck)
363  reachedMore = true;
364  if (mytarg && rolepriority < maxrolepriority) {
365  return false;
366  } else if (reachedLess == true && reachedMore == true) {
367  for (size_t i = 1; i < numTuple; ++i)
368  if (unkey > maxinnerrangeless[i] && unkey < maxinnerrangemore[i]) {
369  maxinnerrangeless[0] = maxinnerrangeless[i];
370  maxinnerrangemore[0] = maxinnerrangemore[i];
371  reachedLess = false;
372  reachedMore = false;
373  }
374  }
375  }
376  }
377  return ShouldTargetUnit( un, distance );
378  }
379  bool ShouldTargetUnit( Unit *un, float distance )
380  {
381  if (un->CloakVisible() > .8) {
382  float rangetotarget = distance;
383  float rel0 = parent->getRelation( un );
384  float rel[] = {
385  rel0 //not initialized until after array
386  , (parentparent ? parentparent->getRelation( un ) : rel0) //not initialized till after array
387  };
388  float relationship = rel0;
389  for (unsigned int i = 1; i < sizeof (rel)/sizeof (*rel); i++)
390  if (rel[i] < relationship)
391  relationship = rel[i];
392  char rp = 31;
393  float tmp = Priority( parent, un, gunrange, rangetotarget, relationship, &rp );
394  if (tmp > priority) {
395  mytarg = un;
396  priority = tmp;
397  rolepriority = rp;
398  }
399  for (vector< TurretBin >::iterator k = tbin->begin(); k != tbin->end(); ++k) {
400  if (rangetotarget > k->maxrange)
401  break;
402  const char tprior = ROLES::getPriority( k->turret[0].tur->attackPreference() )[un->unitRole()];
403  if (relationship < 0) {
404  if (tprior < 16) {
405  k->listOfTargets[0].push_back( TargetAndRange( un, rangetotarget, relationship ) );
406  numtargets++;
407  } else if (tprior < 31) {
408  k->listOfTargets[1].push_back( TargetAndRange( un, rangetotarget, relationship ) );
409  numtargets++;
410  }
411  }
412  }
413  }
414  return (maxtargets == 0) || (numtargets < maxtargets);
415  }
416 };
417 
418 int numpolled[2] = {0, 0}; //number of units that searched for a target
419 
420 int prevpollindex[2] = {10000, 10000}; //previous number of units touched (doesn't need to be precise)
421 
422 int pollindex[2] = {1, 1}; //current count of number of units touched (doesn't need to be precise) -- used for "fairness" heuristic
423 
424 void FireAt::ChooseTargets( int numtargs, bool force )
425 {
426  float gunspeed, gunrange, missilerange;
427  parent->getAverageGunSpeed( gunspeed, gunrange, missilerange );
428  static float targettimer = UniverseUtil::GetGameTime(); //timer used to determine passage of physics frames
429  static float mintimetoswitch =
430  XMLSupport::parse_float( vs_config->getVariable( "AI", "Targetting", "MinTimeToSwitchTargets", "3" ) );
431  static float minnulltimetoswitch =
432  XMLSupport::parse_float( vs_config->getVariable( "AI", "Targetting", "MinNullTimeToSwitchTargets", "5" ) );
433  static int minnumpollers =
434  float_to_int( XMLSupport::parse_float( vs_config->getVariable( "AI", "Targetting", "MinNumberofpollersperframe", "5" ) ) ); //maximum number of vessels allowed to search for a target in a given physics frame
435  static int maxnumpollers =
436  float_to_int( XMLSupport::parse_float( vs_config->getVariable( "AI", "Targetting", "MaxNumberofpollersperframe", "49" ) ) ); //maximum number of vessels allowed to search for a target in a given physics frame
437  static int numpollers[2] = {maxnumpollers, maxnumpollers};
438 
439  static float nextframenumpollers[2] = {maxnumpollers, maxnumpollers};
440  if (lastchangedtarg+mintimetoswitch > 0)
441  return; //don't switch if switching too soon
442 
443  Unit *curtarg = parent->Target();
444  int hastarg = (curtarg == NULL) ? 0 : 1;
445  //Following code exists to limit the number of craft polling for a target in a given frame - this is an expensive operation, and needs to be spread out, or there will be pauses.
446  static float simatom = XMLSupport::parse_float( vs_config->getVariable( "general", "simulation_atom", "0.1" ) );
447  if ( ( UniverseUtil::GetGameTime() )-targettimer >= simatom*.99 ) {
448  //Check if one or more physics frames have passed
449  numpolled[0] = numpolled[1] = 0; //reset counters
450  prevpollindex[0] = pollindex[0];
451  prevpollindex[1] = pollindex[1];
452  pollindex[hastarg] = 0;
453  targettimer = UniverseUtil::GetGameTime();
454  numpollers[0] = float_to_int( nextframenumpollers[0] );
455  numpollers[1] = float_to_int( nextframenumpollers[1] );
456  }
457  pollindex[hastarg]++; //count number of craft touched - will use in the next physics frame to spread out the vessels actually chosen to be processed among all of the vessels being touched
458  if (numpolled[hastarg] > numpollers[hastarg]) //over quota, wait until next physics frame
459  return;
460  if ( !( pollindex[hastarg]%( (prevpollindex[hastarg]/numpollers[hastarg])+1 ) ) ) //spread out, in modulo fashion, the possibility of changing one's target. Use previous physics frame count of craft to estimate current number of craft
461  numpolled[hastarg]++; //if a more likely candidate, we're going to search for a target.
462  else
463  return; //skipped to achieve better fairness - see comment on modulo distribution above
464  if (curtarg)
465  if ( isJumpablePlanet( curtarg ) )
466  return;
467  bool wasnull = (curtarg == NULL);
468  Flightgroup *fg = parent->getFlightgroup();
469  lastchangedtarg = 0+targrand.uniformInc( 0, 1 )*mintimetoswitch; //spread out next valid time to switch targets - helps to ease per-frame loads.
470  if (fg) {
471  if ( !fg->directive.empty() )
472  if ( curtarg != NULL && ( *fg->directive.begin() ) == toupper( *fg->directive.begin() ) )
473  return;
474  }
475  //not allowed to switch targets
476  numprocessed++;
477  vector< TurretBin >tbin;
478  Unit *su = NULL;
479  un_iter subun = parent->getSubUnits();
480  for (; (su = *subun) != NULL; ++subun) {
481  static unsigned int inert = ROLES::getRole( "INERT" );
482  static unsigned int pointdef = ROLES::getRole( "POINTDEF" );
483  static bool assignpointdef =
484  XMLSupport::parse_bool( vs_config->getVariable( "AI", "Targetting", "AssignPointDef", "true" ) );
485  if ( (su->attackPreference() != pointdef) || assignpointdef ) {
486  if (su->attackPreference() != inert) {
487  AssignTBin( su, tbin );
488  } else {
489  Unit *ssu = NULL;
490  for (un_iter subturret = su->getSubUnits(); ( ssu = (*subturret) ); ++subturret)
491  AssignTBin( ssu, tbin );
492  }
493  }
494  }
495  std::sort( tbin.begin(), tbin.end() );
496  float efrel = 0;
497  float mytargrange = FLT_MAX;
498  static float unitRad =
499  XMLSupport::parse_float( vs_config->getVariable( "AI", "Targetting", "search_extra_radius", "1000" ) ); //Maximum target radius that is guaranteed to be detected
500  static char maxrolepriority =
501  XMLSupport::parse_int( vs_config->getVariable( "AI", "Targetting", "search_max_role_priority", "16" ) );
502  static int maxtargets = XMLSupport::parse_int( vs_config->getVariable( "AI", "Targetting", "search_max_candidates", "64" ) ); //Cutoff candidate count (if that many hostiles found, stop search - performance/quality tradeoff, 0=no cutoff)
503  UnitWithinRangeLocator< ChooseTargetClass< 2 > >unitLocator( parent->GetComputerData().radar.maxrange, unitRad );
504  StaticTuple< float, 2 >maxranges;
505 
506  maxranges[0] = gunrange;
507  maxranges[1] = missilerange;
508  if ( tbin.size() )
509  maxranges[0] = (tbin[0].maxrange > gunrange ? tbin[0].maxrange : gunrange);
510  double pretable = queryTime();
511  unitLocator.action.init( this, parent, gunrange, &tbin, maxranges, maxrolepriority, maxtargets );
512  static int gcounter = 0;
513  static int min_rechoose_interval = XMLSupport::parse_int( vs_config->getVariable( "AI", "min_rechoose_interval", "128" ) );
514  if (curtarg) {
515  if (gcounter++ < min_rechoose_interval || rand()/8 < RAND_MAX/9) {
516  //in this case only look at potentially *interesting* units rather than huge swaths of nearby units...including target, threat, players, and leader's target
517  unitLocator.action.ShouldTargetUnit( curtarg, UnitUtil::getDistance( parent, curtarg ) );
518  unsigned int np = _Universe->numPlayers();
519  for (unsigned int i = 0; i < np; ++i) {
520  Unit *playa = _Universe->AccessCockpit( i )->GetParent();
521  if (playa)
522  unitLocator.action.ShouldTargetUnit( playa, UnitUtil::getDistance( parent, playa ) );
523  }
524  Unit *lead = UnitUtil::getFlightgroupLeader( parent );
525  if (lead != NULL && lead != parent && ( lead = lead->Target() ) != NULL)
526  unitLocator.action.ShouldTargetUnit( lead, UnitUtil::getDistance( parent, lead ) );
527  Unit *threat = parent->Threat();
528  if (threat)
529  unitLocator.action.ShouldTargetUnit( threat, UnitUtil::getDistance( parent, threat ) );
530  } else {
531  gcounter = 0;
532  }
533  }
534  if (unitLocator.action.mytarg == NULL) //decided to rechoose or did not have initial target
535  findObjects(
536  _Universe->activeStarSystem()->collidemap[Unit::UNIT_ONLY], parent->location[Unit::UNIT_ONLY], &unitLocator );
537  Unit *mytarg = unitLocator.action.mytarg;
538  targetpick += queryTime()-pretable;
539  if (mytarg) {
540  efrel = parent->getRelation( mytarg );
541  mytargrange = UnitUtil::getDistance( parent, mytarg );
542  }
543  TargetAndRange my_target( mytarg, mytargrange, efrel );
544  for (vector< TurretBin >::iterator k = tbin.begin(); k != tbin.end(); ++k)
545  k->AssignTargets( my_target, parent->cumulative_transformation_matrix );
546  parent->LockTarget( false );
547  if (wasnull) {
548  if (mytarg) {
549  nextframenumpollers[hastarg] += 2;
550  if (nextframenumpollers[hastarg] > maxnumpollers)
551  nextframenumpollers[hastarg] = maxnumpollers;
552  } else {
553  lastchangedtarg += targrand.uniformInc( 0, 1 )*minnulltimetoswitch;
554  nextframenumpollers[hastarg] -= .05;
555  if (nextframenumpollers[hastarg] < minnumpollers)
556  nextframenumpollers[hastarg] = minnumpollers;
557  }
558  } else {
559  if (parent->Target() != mytarg) {
560  nextframenumpollers[hastarg] += 2;
561  if (nextframenumpollers[hastarg] > maxnumpollers)
562  nextframenumpollers[hastarg] = maxnumpollers;
563  } else {
564  nextframenumpollers[hastarg] -= .01;
565  if (nextframenumpollers[hastarg] < minnumpollers)
566  nextframenumpollers[hastarg] = minnumpollers;
567  }
568  }
569  parent->Target( mytarg );
570  parent->LockTarget( true );
571  SignalChosenTarget();
572 }
573 
574 bool FireAt::ShouldFire( Unit *targ, bool &missilelock )
575 {
576  float dist;
577  if (!targ) {
578  return false;
579 
580  static int test = 0;
581  if (test++%1000 == 1)
582  VSFileSystem::vs_fprintf( stderr, "lost target" );
583  }
584  float gunspeed, gunrange, missilerange;
585  parent->getAverageGunSpeed( gunspeed, gunrange, missilerange );
586  float angle = parent->cosAngleTo( targ, dist, parent->GetComputerData().itts ? gunspeed : FLT_MAX, gunrange, false );
587  missilelock = false;
588  targ->Threaten( parent, angle/(dist < .8 ? .8 : dist) );
589  if ( targ == parent->Target() )
590  distance = dist;
591  static float firewhen = XMLSupport::parse_float( vs_config->getVariable( "AI", "Firing", "InWeaponRange", "1.2" ) );
592  static float fireangle_minagg =
594  "Firing",
595  "MaximumFiringAngle.minagg",
596  "10" ) )/180. ); //Roughly 10 degrees
597  static float fireangle_maxagg =
599  "Firing",
600  "MaximumFiringAngle.maxagg",
601  "18" ) )/180. ); //Roughly 18 degrees
602  float temp = parent->TrackingGuns( missilelock );
603  bool isjumppoint = targ->isUnit() == PLANETPTR && ( (Planet*) targ )->GetDestinations().empty() == false;
604  float fangle = (fireangle_minagg+fireangle_maxagg*agg)/(1.0f+agg);
605  bool retval =
606  ( (dist < firewhen)
607  && ( (angle > fangle) || ( temp && (angle > temp) ) || ( missilelock && (angle > 0) ) ) ) && !isjumppoint;
608  if (retval) {
609  if ( Cockpit::tooManyAttackers() ) {
610  Cockpit *player = _Universe->isPlayerStarship( targ );
611  if (player) {
612  static int max_attackers = XMLSupport::parse_int( vs_config->getVariable( "AI", "max_player_attackers", "0" ) );
613  int attackers = player->number_of_attackers;
614  if (attackers > max_attackers && max_attackers > 0) {
615  static float attacker_switch_time =
616  XMLSupport::parse_float( vs_config->getVariable( "AI", "attacker_switch_time", "15" ) );
617  int curtime = (int) fmod( floor( UniverseUtil::GetGameTime()/attacker_switch_time ), (float) (1<<24) );
618  int seed = ( ( ( (size_t) parent )&0xffffffff )^curtime );
619  static VSRandom decide( seed );
620  decide.init_genrand( seed );
621  if (decide.genrand_int31()%attackers >= max_attackers) {
622  return false;
623  }
624  }
625  }
626  }
627  }
628  return retval;
629 }
630 
632 {
633 #ifdef ORDERDEBUG
634  VSFileSystem::vs_fprintf( stderr, "fire%x\n", this );
635  fflush( stderr );
636 #endif
637 }
638 
639 unsigned int FireBitmask( Unit *parent, bool shouldfire, bool firemissile )
640 {
641  unsigned int firebitm = ROLES::EVERYTHING_ELSE;
642  Unit *un = parent->Target();
643  if (un) {
644  firebitm = ( 1<<un->unitRole() );
645 
646  static bool AlwaysFireAutotrackers =
647  XMLSupport::parse_bool( vs_config->getVariable( "AI", "AlwaysFireAutotrackers", "true" ) );
648  if (shouldfire)
649  firebitm |= ROLES::FIRE_GUNS;
650  if (AlwaysFireAutotrackers && !shouldfire) {
651  firebitm |= ROLES::FIRE_GUNS;
652  firebitm |= ROLES::FIRE_ONLY_AUTOTRACKERS;
653  }
654  if (firemissile)
655  firebitm = ROLES::FIRE_MISSILES; //stops guns
656  }
657  return firebitm;
658 }
659 
660 void FireAt::FireWeapons( bool shouldfire, bool lockmissile )
661 {
662  static float missiledelay = XMLSupport::parse_float( vs_config->getVariable( "AI", "MissileGunDelay", "4" ) );
663  bool fire_missile = lockmissile && rand() < RAND_MAX*missileprobability*SIMULATION_ATOM;
664  delay += SIMULATION_ATOM;
665  if ( shouldfire && delay < parent->pilot->getReactionTime() )
666  return;
667  else if (!shouldfire)
668  delay = 0;
669  if (fire_missile)
670  lastmissiletime = UniverseUtil::GetGameTime();
671  else if (UniverseUtil::GetGameTime()-lastmissiletime < missiledelay && !fire_missile)
672  return;
673  parent->Fire( FireBitmask( parent, shouldfire, fire_missile ), true );
674 }
675 
677 {
678  bool istargetjumpableplanet = targ->isUnit() == PLANETPTR;
679  if (istargetjumpableplanet) {
680  istargetjumpableplanet = ( !( (Planet*) targ )->GetDestinations().empty() ) && (parent->GetJumpStatus().drive >= 0);
681  }
682  return istargetjumpableplanet;
683 }
684 
685 using std::string;
686 void FireAt::PossiblySwitchTarget( bool unused )
687 {
688  static float targettime = XMLSupport::parse_float( vs_config->getVariable( "AI", "Targetting", "TimeUntilSwitch", "20" ) );
689  if ( (targettime <= 0) || (vsrandom.uniformInc( 0, 1 ) < SIMULATION_ATOM/targettime) ) {
690  bool ct = true;
691  Flightgroup *fg;
692  if ( ( fg = parent->getFlightgroup() ) )
693  if (fg->directive.find( "." ) != string::npos)
694  ct = (parent->Target() == NULL);
695  if (ct)
696  ChooseTarget();
697  }
698 }
699 
701 {
702  lastchangedtarg -= SIMULATION_ATOM;
703  bool missilelock = false;
704  bool tmp = done;
705  Order::Execute();
706  done = tmp;
707  Unit *targ;
708  if (parent->isUnit() == UNITPTR) {
709  static float cont_update_time = XMLSupport::parse_float( vs_config->getVariable( "AI", "ContrabandUpdateTime", "1" ) );
710  if (rand() < RAND_MAX*SIMULATION_ATOM/cont_update_time)
711  UpdateContrabandSearch();
712  static float cont_initiate_time = XMLSupport::parse_float( vs_config->getVariable( "AI", "CommInitiateTime", "300" ) );
713  if ( (float) rand() < ( (float) RAND_MAX*(SIMULATION_ATOM/cont_initiate_time) ) ) {
714  static float contraband_initiate_time =
715  XMLSupport::parse_float( vs_config->getVariable( "AI", "ContrabandInitiateTime", "3000" ) );
716  static float comm_to_player = XMLSupport::parse_float( vs_config->getVariable( "AI", "CommToPlayerPercent", ".05" ) );
717  static float comm_to_target = XMLSupport::parse_float( vs_config->getVariable( "AI", "CommToTargetPercent", ".25" ) );
718  static float contraband_to_player =
719  XMLSupport::parse_float( vs_config->getVariable( "AI", "ContrabandToPlayerPercent", ".98" ) );
720  static float contraband_to_target =
721  XMLSupport::parse_float( vs_config->getVariable( "AI", "ContrabandToTargetPercent", "0.001" ) );
722 
723  unsigned int modulo = ( (unsigned int) (contraband_initiate_time/cont_initiate_time) );
724  if (modulo < 1)
725  modulo = 1;
726  if (rand()%modulo)
727  RandomInitiateCommunication( comm_to_player, comm_to_target );
728  else
729  InitiateContrabandSearch( contraband_to_player, contraband_to_target );
730  }
731  }
732  bool shouldfire = false;
733  bool istargetjumpableplanet = false;
734  if ( ( targ = parent->Target() ) ) {
735  istargetjumpableplanet = isJumpablePlanet( targ );
736  if (targ->CloakVisible() > .8 && targ->GetHull() >= 0) {
737  had_target = true;
738  if (parent->GetNumMounts() > 0)
739  if (!istargetjumpableplanet)
740  shouldfire |= ShouldFire( targ, missilelock );
741  } else {
742  if (had_target) {
743  had_target = false;
744  lastchangedtarg = -100000;
745  }
746  ChooseTarget();
747  }
748  } else if (had_target) {
749  had_target = false;
750  lastchangedtarg = -100000;
751  }
752  PossiblySwitchTarget( istargetjumpableplanet );
753  if ( (!istargetjumpableplanet) && parent->GetNumMounts() > 0 )
754  FireWeapons( shouldfire, missilelock );
755 }
756