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
aggressive.cpp
Go to the documentation of this file.
1 #include "config.h"
2 #include "aggressive.h"
3 #include "event_xml.h"
4 #include "script.h"
5 #include <list>
6 #include <vector>
7 #include "vs_globals.h"
8 #include "config_xml.h"
9 #include "xml_support.h"
10 #include "cmd/unit_generic.h"
11 #include "communication.h"
12 #include "cmd/script/flightgroup.h"
13 #include "flybywire.h"
14 #include "hard_coded_scripts.h"
15 #include "cmd/script/mission.h"
16 #include "gfx/cockpit_generic.h"
17 #include "lin_time.h"
18 #include "faction_generic.h"
19 #include "cmd/role_bitmask.h"
20 #include "cmd/unit_util.h"
21 #include "warpto.h"
22 #include "cmd/csv.h"
23 #include "universe_util.h"
24 #include "vs_random.h"
25 #include "python/python_compile.h"
26 #include "cmd/unit_find.h"
27 #include "faction_generic.h"
28 #include "docking.h"
29 
30 extern double aggfire;
31 
32 using namespace Orders;
33 
35  EnumMap::Pair( "AggressiveAI", AggressiveAI::AGGAI ),
38  EnumMap::Pair( "MeterDistance", AggressiveAI::METERDISTANCE ),
47  EnumMap::Pair( "FShield_Heal_Rate", AggressiveAI::FSHIELD_HEAL_RATE ),
48  EnumMap::Pair( "BShield_Heal_Rate", AggressiveAI::BSHIELD_HEAL_RATE ),
49  EnumMap::Pair( "LShield_Heal_Rate", AggressiveAI::LSHIELD_HEAL_RATE ),
50  EnumMap::Pair( "RShield_Heal_Rate", AggressiveAI::RSHIELD_HEAL_RATE ),
51  EnumMap::Pair( "FArmor_Heal_Rate", AggressiveAI::FARMOR_HEAL_RATE ),
52  EnumMap::Pair( "BArmor_Heal_Rate", AggressiveAI::BARMOR_HEAL_RATE ),
53  EnumMap::Pair( "LArmor_Heal_Rate", AggressiveAI::LARMOR_HEAL_RATE ),
54  EnumMap::Pair( "RArmor_Heal_Rate", AggressiveAI::RARMOR_HEAL_RATE ),
55  EnumMap::Pair( "Hull_Heal_Rate", AggressiveAI::HULL_HEAL_RATE ),
56  EnumMap::Pair( "Target_Faces_You", AggressiveAI::TARGET_FACES_YOU ),
57  EnumMap::Pair( "Target_In_Front_Of_You", AggressiveAI::TARGET_IN_FRONT_OF_YOU ),
59  EnumMap::Pair( "Target_Going_Your_Direction", AggressiveAI::TARGET_GOING_YOUR_DIRECTION )
60 };
62 
63 using std::pair;
64 
65 vsUMap< string, AIEvents::ElemAttrMap* >logic;
66 
67 extern bool CheckAccessory( Unit *tur );
68 
69 static void TurretFAW( Unit *parent )
70 {
71  un_iter iter = parent->getSubUnits();
72  Unit *un;
73  while ( NULL != (un = *iter) ) {
74  if ( !CheckAccessory( un ) ) {
75  un->EnqueueAIFirst( new Orders::FireAt( 15.0f ) );
76  un->EnqueueAIFirst( new Orders::FaceTarget( false, 3 ) );
77  }
78  TurretFAW( un );
79  ++iter;
80  }
81 }
82 
83 static vsUMap< string, string >getAITypes()
84 {
85  vsUMap< string, string >ret;
87  VSError err = f.OpenReadOnly( "VegaPersonalities.csv", AiFile );
88  if (err <= Ok) {
89  CSVTable table( f, f.GetRoot() );
90  vsUMap< std::string, int >::iterator browser = table.rows.begin();
91  for (; browser != table.rows.end(); ++browser) {
92  string rowname = (*browser).first;
93  CSVRow row( &table, rowname );
94  for (unsigned int i = 1; i < table.key.size(); ++i) {
95  string hasher = rowname;
96  if (i != 1)
97  hasher = rowname+"%"+table.key[i];
98  string rawrow = row[i];
99  if (rawrow.length() > 0)
100  ret[hasher] = rawrow;
101  }
102  }
103  f.Close();
104  }
105  return ret;
106 }
107 
108 static string select_from_space_list( string inp, unsigned int seed )
109 {
110  if (inp.length() == 0)
111  return "";
112  int count = 1;
113  string::size_type len = inp.length();
114  for (unsigned int i = 0; i < len; ++i)
115  if (inp[i] == ' ')
116  count++;
117  count = seed%count;
118  int ncount = 0;
119  unsigned int j;
120  for (j = 0; j < len; ++j) {
121  if (inp[j] == ' ')
122  ncount++;
123  if (ncount >= count)
124  break;
125  }
126  if (inp[j] == ' ')
127  j++;
128  inp = inp.substr( j );
129  if ( ( len = inp.find( " " ) ) != string::npos )
130  inp = inp.substr( 0, len );
131  return inp;
132 }
133 
135  int faction,
136  string unittype,
137  vsUMap< string, AIEvents::ElemAttrMap* > &mymap,
138  int personalityseed )
139 {
140  string append = "agg";
141  static vsUMap< string, string >myappend = getAITypes();
142  vsUMap< string, string >::iterator iter;
143  string factionname = FactionUtil::GetFaction( faction );
144  if ( ( iter = myappend.find( factionname+"%"+unittype ) ) != myappend.end() )
145  append = select_from_space_list( (*iter).second, personalityseed );
146  else if ( ( iter = myappend.find( factionname ) ) != myappend.end() )
147  append = select_from_space_list( (*iter).second, personalityseed );
148  if (append.length() == 0) append = "agg";
149  string hashname = name+"."+append;
150  vsUMap< string, AIEvents::ElemAttrMap* >::iterator i = mymap.find( hashname );
151  if ( i == mymap.end() ) {
153  string filename( name+"."+append+".xml" );
154  AIEvents::LoadAI( filename.c_str(), *attr, FactionUtil::GetFaction( faction ) );
155  mymap.insert( pair< string, AIEvents::ElemAttrMap* > ( hashname, attr ) );
156  return attr;
157  }
158  return i->second;
159 }
160 
162  int faction,
163  string unittype,
164  bool interrupt,
165  int personalityseed )
166 {
167  return getLogicOrInterrupt( name, faction, unittype, logic, personalityseed );
168 }
169 
170 static AIEvents::ElemAttrMap * getProperScript( Unit *me, Unit *targ, bool interrupt, int personalityseed )
171 {
172  if (!me || !targ) {
173  string nam = "eject";
174  int fac = 0;
175  if (me) {
176  fac = me->faction;
177  nam = me->name;
178  }
179  return getProperLogicOrInterruptScript( "default", fac, nam, interrupt, personalityseed );
180  }
182  targ->unitRole() ), me->faction, me->name, interrupt,
183  personalityseed );
184 }
185 
186 inline std::string GetRelationshipColor( float rel )
187 {
188  if (rel >= 1)
189  return "#00FF00";
190  if (rel <= -1)
191  return "#FF0000";
192  rel += 1.;
193  rel /= 2.;
194  char str[20]; //Just in case all 8 digits of both #s end up inside the string for some reason.
195  sprintf( str, "#%2X%2X00", (int) ( (1-rel)*256 ), (int) (rel*256) );
196  return str;
197 }
198 
199 unsigned int DoSpeech( Unit *un, Unit *player_un, const FSM::Node &node )
200 {
201  unsigned int dummy = 0;
202  string speech = node.GetMessage( dummy );
203  string myname( "[Static]" );
204  if (un != NULL) {
205  myname = un->isUnit() == PLANETPTR ? un->name : un->getFullname();
206  Flightgroup *fg = un->getFlightgroup();
207  if (fg && fg->name != "base" && fg->name != "Base")
208  myname = fg->name+" "+XMLSupport::tostring( un->getFgSubnumber() )+", "+un->getFullname();
209  else if (myname.length() == 0)
210  myname = un->name;
211  if (player_un != NULL) {
212  if (player_un == un) {
213  myname = std::string( "#0033FF" )+myname+"#000000";
214  } else {
215  float rel = un->getRelation( player_un );
216  myname = GetRelationshipColor( rel )+myname+"#000000";
217  }
218  }
219  }
220  mission->msgcenter->add( myname, "all", GetRelationshipColor( node.messagedelta*10 )+speech+"#000000" ); //multiply by 2 so colors are easier to tell
221  return dummy;
222 }
223 
224 void LeadMe( Unit *un, string directive, string speech, bool changetarget )
225 {
226  if (un != NULL) {
227  for (unsigned int i = 0; i < _Universe->numPlayers(); i++) {
228  Unit *pun = _Universe->AccessCockpit( i )->GetParent();
229  if (pun)
230  if ( pun->getFlightgroup() == un->getFlightgroup() )
231  DoSpeech( un, pun, FSM::Node::MakeNode( speech, .1 ) );
232  }
233  Flightgroup *fg = un->getFlightgroup();
234  if (fg) {
235  if (fg->leader.GetUnit() != un)
236  if ( ( !_Universe->isPlayerStarship( fg->leader.GetUnit() ) ) || _Universe->isPlayerStarship( un ) )
237  fg->leader.SetUnit( un );
238  fg->directive = directive;
239  if (changetarget)
240  fg->target.SetUnit( un->Target() );
241  if ( (directive == "") )
242  fg->target.SetUnit( NULL );
243  }
244  }
245 }
246 
247 static float aggressivity = 2.01;
248 static int randomtemp;
249 AggressiveAI::AggressiveAI( const char *filename, Unit *target ) : FireAt()
250  , logic( getProperScript( NULL, NULL, "default", randomtemp = rand() ) )
251 {
252  currentpriority = 0;
253  last_jump_time = 0;
254  nav = QVector( 0, 0, 0 );
256  last_jump_distance = FLT_MAX;
257  interruptcurtime = 0;
258  creationtime = getNewTime();
259  jump_time_check = 1;
260  last_time_insys = true;
261  logiccurtime = logic->maxtime; //set it to the time allotted
262  obedient = true;
263  if (aggressivity == 2.01) {
264  static float defagg = XMLSupport::parse_float( vs_config->getVariable( "unit", "aggressivity", "2" ) );
265  aggressivity = defagg;
266  }
267  if (target != NULL)
268  AttachOrder( target );
269  last_directive = filename;
270 }
271 
273 {
274  FireAt::SetParent( parent1 );
275  string::size_type which = last_directive.find( "|" );
276  string filename( string( "default.agg.xml" ) );
277  string interruptname( string( "default.int.xml" ) );
278  if (which != string::npos) {
279  filename = last_directive.substr( 0, which );
280  interruptname = last_directive.substr( which+1 );
281  }
282  last_directive = "b"; //prevent escort race condition
283 
284  //INIT stored stuff
286  Fshield_rate_old = 0.0;
289  Bshield_rate_old = 0.0;
292  Lshield_rate_old = 0.0;
295  Rshield_rate_old = 0.0;
297  Farmour_prev = 1.0;
298  Farmour_rate_old = 0.0;
300  Barmour_prev = 1.0;
301  Barmour_rate_old = 0.0;
303  Larmour_prev = 1.0;
304  Larmour_rate_old = 0.0;
306  Rarmour_prev = 1.0;
307  Rarmour_rate_old = 0.0;
310  Hull_rate_old = 0.0;
312 }
313 
315 {
316  if (parent)
319 }
320 
322 {
323  if (item.script.length() != 0) {
324  Order *tmp = new ExecuteFor( new AIScript( item.script.c_str() ), item.timetofinish );
325  EnqueueOrder( tmp );
326  return true;
327  } else {
328  return false;
329  }
330 }
331 
333 {
334  float value = 0.0;
335 
336  static float game_speed = XMLSupport::parse_float( vs_config->getVariable( "physics", "game_speed", "1" ) );
337  switch ( abs( item.type ) )
338  {
339  case DISTANCE:
340  value = distance;
341  break;
342  case METERDISTANCE:
343  {
344  Unit *targ = parent->Target();
345  if (targ) {
346  Vector PosDifference = targ->Position().Cast()-parent->Position().Cast();
347  float pdmag = PosDifference.Magnitude();
348  value = ( pdmag-parent->rSize()-targ->rSize() );
349  float myvel = PosDifference.Dot( parent->GetVelocity()-targ->GetVelocity() )/value;
350  if (myvel > 0)
351  value -= myvel*myvel/( 2*( parent->Limits().retro/parent->GetMass() ) );
352  } else {
353  value = 10000;
354  }
355  value /= game_speed; /*game_accel*/
356  break;
357  }
358  case THREAT:
360  break;
361  case FSHIELD:
362  value = parent->graphicOptions.InWarp ? 1 : parent->FShieldData();
363  break;
364  case BSHIELD:
365  value = parent->graphicOptions.InWarp ? 1 : parent->BShieldData();
366  break;
367  case HULL:
368  {
369  value = parent->GetHullPercent();
370  break;
371  }
372  case LSHIELD:
373  value = parent->graphicOptions.InWarp ? 1 : parent->LShieldData();
374  break;
375  case RSHIELD:
376  value = parent->graphicOptions.InWarp ? 1 : parent->RShieldData();
377  break;
378  case FSHIELD_HEAL_RATE:
379  {
380  double delta_t = UniverseUtil::GetGameTime()-Fshield_prev_time;
381  if (delta_t > 0.5) {
382  //0.5 = reaction time limit for hit rate
383  double delta_v = parent->graphicOptions.InWarp ? 1 : parent->FShieldData()-Fshield_prev;
384  value = delta_v/delta_t;
385  Fshield_rate_old = value;
388  } else {
389  value = Fshield_rate_old;
390  }
391  break;
392  }
393  case BSHIELD_HEAL_RATE:
394  {
395  double delta_t = UniverseUtil::GetGameTime()-Bshield_prev_time;
396  if (delta_t > 0.5) {
397  //0.5 = reaction time limit for hit rate
398  double delta_v = parent->graphicOptions.InWarp ? 1 : parent->BShieldData()-Bshield_prev;
399  value = delta_v/delta_t;
400  Bshield_rate_old = value;
403  } else {
404  value = Bshield_rate_old;
405  }
406  break;
407  }
408  case LSHIELD_HEAL_RATE:
409  {
410  double delta_t = UniverseUtil::GetGameTime()-Lshield_prev_time;
411  if (delta_t > 0.5) {
412  //0.5 = reaction time limit for hit rate
413  double delta_v = parent->graphicOptions.InWarp ? 1 : parent->LShieldData()-Lshield_prev;
414  value = delta_v/delta_t;
415  Lshield_rate_old = value;
418  } else {
419  value = Lshield_rate_old;
420  }
421  break;
422  }
423  case RSHIELD_HEAL_RATE:
424  {
425  double delta_t = UniverseUtil::GetGameTime()-Rshield_prev_time;
426  if (delta_t > 0.5) {
427  //0.5 = reaction time limit for hit rate
428  double delta_v = parent->graphicOptions.InWarp ? 1 : parent->RShieldData()-Rshield_prev;
429  value = delta_v/delta_t;
430  Rshield_rate_old = value;
433  } else {
434  value = Rshield_rate_old;
435  }
436  break;
437  }
438  case FARMOR_HEAL_RATE:
439  value = 0.0;
440  break;
441  case BARMOR_HEAL_RATE:
442  value = 0.0;
443  break;
444  case LARMOR_HEAL_RATE:
445  value = 0.0;
446  break;
447  case RARMOR_HEAL_RATE:
448  value = 0.5;
449  break;
450  case HULL_HEAL_RATE:
451  {
452  double delta_t = UniverseUtil::GetGameTime()-Hull_prev_time;
453  if (delta_t > 0.5) {
454  //0.5 = reaction time limit for hit rate
455  double delta_v = parent->GetHullPercent()-Hull_prev;
456  value = delta_v/delta_t;
457  Hull_rate_old = value;
460  } else {
461  value = Hull_rate_old;
462  }
463  break;
464  }
465  case TARGET_FACES_YOU:
466  {
467  value = 0.0;
468  Unit *targ = parent->Target();
469  if (targ) {
470  Vector Q;
471  Vector P;
472  Vector R;
473 
474  Vector PosDelta = ( parent->Position() )-( targ->Position() );
475  PosDelta = PosDelta/PosDelta.Magnitude();
476  targ->GetOrientation( Q, P, R );
477  value = PosDelta.Dot( R );
478  }
479  break;
480  }
482  {
483  value = 0.0;
484  Unit *targ = parent->Target();
485  if (targ) {
486  Vector Q;
487  Vector P;
488  Vector S;
489 
490  Vector PosDelta = ( targ->Position() )-( parent->Position() );
491  PosDelta = PosDelta/PosDelta.Magnitude();
492  parent->GetOrientation( Q, P, S );
493  value = PosDelta.Dot( S );
494  }
495  break;
496  }
498  {
499  value = 0.0;
500  Unit *targ = parent->Target();
501  if (targ) {
502  Vector Q;
503  Vector P;
504  Vector R;
505  Vector S;
506 
507  parent->GetOrientation( Q, P, S );
508  targ->GetOrientation( Q, P, R );
509  value = S.Dot( R );
510  }
511  break;
512  }
513  case FACING:
514  return queryType( Order::FACING ) == NULL;
515 
516  case MOVEMENT:
517  return queryType( Order::MOVEMENT ) == NULL;
518 
519  case RANDOMIZ:
520  value = ( (float) rand() )/RAND_MAX;
521  break;
522  default:
523  return false;
524  }
525  return item.Eval( value );
526 }
527 
529 {
530  //go through the logic.
531  bool retval = false;
532  std::vector< std::list< AIEvents::AIEvresult > >::iterator i = logi.result.begin();
533  for (; i != logi.result.end(); i++) {
534  std::list< AIEvents::AIEvresult >::iterator j;
535  bool trueit = true;
536  for (j = i->begin(); j != i->end(); j++)
537  if ( !ProcessLogicItem( *j ) ) {
538  trueit = false;
539  break;
540  }
541  if ( trueit && j == i->end() ) {
542  //do it
543  if ( j != i->begin() )
544  j--;
545  if ( j != i->end() ) {
546  float priority = (*j).priority;
547  if (priority > this->currentpriority || !inter) {
548  if (inter) {
551  }
552  j = i->begin();
553  logiccurtime = 0;
554  interruptcurtime = 0;
555  if ( j != i->end() ) {
556  while ( j != i->end() ) {
557  if ( ExecuteLogicItem( *j ) ) {
558  this->currentpriority = priority;
559  logiccurtime += (*j).timetofinish;
560  interruptcurtime += (*j).timetointerrupt;
561  retval = true;
562  }
563  j++;
564  }
565  if (retval) break;
566  }
567  }
568  }
569  }
570  }
571  return retval;
572 }
573 
574 Unit * GetThreat( Unit *parent, Unit *leader )
575 {
576  Unit *th = NULL;
577  Unit *un = NULL;
578  bool targetted = false;
579  float mindist = FLT_MAX;
581  (un = *ui);
582  ++ui)
583  if (parent->getRelation( un ) < 0) {
584  float d = ( un->Position()-leader->Position() ).Magnitude();
585  bool thistargetted = (un->Target() == leader);
586  if ( !th || (thistargetted && !targetted) || ( ( thistargetted || (!targetted) ) && d < mindist ) ) {
587  th = un;
588  targetted = thistargetted;
589  mindist = d;
590  }
591  }
592  return th;
593 }
594 
596 {
597  bool retval = false;
598  if (fg != NULL) {
599  Unit *leader = fg->leader.GetUnit();
600  if ( last_directive.empty() )
602  if (fg->directive != last_directive) {
603  static bool forceobedient = XMLSupport::parse_bool( vs_config->getVariable( "AI", "always_obedient", "true" ) );
604  if (forceobedient)
605  obedient = true;
606  else if ( float( rand() )/RAND_MAX < (obedient ? (1-logic->obedience) : logic->obedience) )
607  obedient = !obedient;
608  if (obedient) {
611  Unit *targ = parent->Target();
612  if (targ) {
613  bool attacking = fg->directive.length() > 0;
614  if (attacking)
615  attacking = tolower( fg->directive[0] ) == 'a';
616  if ( ( !isJumpablePlanet( targ ) ) && attacking == false )
617  parent->Target( NULL );
618  }
619  } else {
620  CommunicationMessage c( parent, leader, NULL, 0 );
621  c.SetCurrentState( c.fsm->GetNoNode(), NULL, 0 );
622  Order *lo = leader->getAIState();
623  if (lo)
624  lo->Communicate( c );
625  }
626  }
627  if (obedient) {
628  void *parentowner;
629  void *leaderowner = leader;
630  parentowner = parent->owner ? parent->owner : parent;
631  leaderowner = leader->owner ? leader->owner : leader;
632  if (fg->directive.find( "k" ) != string::npos || fg->directive.find( "K" ) != string::npos) {
633  Unit *targ = fg->target.GetUnit();
634  bool callme = false;
635  if ( targ && (targ->faction != parent->faction) ) {
636  if ( targ->InCorrectStarSystem( _Universe->activeStarSystem() ) ) {
637  CommunicationMessage c( parent, leader, NULL, 0 );
638  c.SetCurrentState( c.fsm->GetNoNode(), NULL, 0 );
639  if ( parent->InRange( targ, true, false ) ) {
640  if ( targ != parent->Target() )
641  callme = true;
642  parent->Target( targ );
643  parent->SetTurretAI();
644  parent->TargetTurret( targ );
645  //if I am the capship, go into defensive mode.
646  if (parent == leaderowner) {
647  //get in front of me
648  parent->TurretFAW();
649 
650  c.SetCurrentState( c.fsm->GetYesNode(), NULL, 0 );
652  0,
653  0 ),
654  true ), true, false, true );
655  ord->SetParent( parent );
656  ReplaceOrder( ord );
657  //facing forward
658  ord = new Orders::FaceTarget( false, 3 );
659  ord->SetParent( parent );
660  ReplaceOrder( ord );
661  } else {
662  Order *ord = new Orders::FaceTarget( false, 3 );
663  ord->SetParent( parent );
664  ReplaceOrder( ord );
665  c.SetCurrentState( c.fsm->GetYesNode(), NULL, 0 );
666  }
667  } else {
668  c.SetCurrentState( c.fsm->GetNoNode(), NULL, 0 );
669  }
670  if (fg->directive != last_directive) {
671  Order *lo = leader->getAIState();
672  if (lo || callme)
673  lo->Communicate( c );
674  }
675  }
676  }
677  //a is now used for AI, for backward compatibility. do not use for player
678  } else if (fg->directive.find( "a" ) != string::npos || fg->directive.find( "A" ) != string::npos) {
679  Unit *targ = fg->leader.GetUnit();
680  targ = targ != NULL ? targ->Target() : NULL;
681  if (targ) {
682  if ( targ->InCorrectStarSystem( _Universe->activeStarSystem() ) ) {
683  CommunicationMessage c( parent, leader, NULL, 0 );
684  if ( parent->InRange( targ, true, false ) ) {
685  parent->Target( targ );
686  parent->TargetTurret( targ );
687  c.SetCurrentState( c.fsm->GetYesNode(), NULL, 0 );
688  } else {
689  c.SetCurrentState( c.fsm->GetNoNode(), NULL, 0 );
690  }
691  if (fg->directive != last_directive) {
692  Order *lo = leader->getAIState();
693  if (lo)
694  lo->Communicate( c );
695  }
696  }
697  }
698  } else if (fg->directive.find( "f" ) != string::npos || fg->directive.find( "F" ) != string::npos) {
699  if (leader != NULL) {
700  if ( leader->InCorrectStarSystem( _Universe->activeStarSystem() ) ) {
701  retval = true;
702  if ( fg->directive != last_directive || (!last_time_insys) ) {
703  last_time_insys = true;
704  CommunicationMessage c( parent, leader, NULL, 0 );
705  c.SetCurrentState( c.fsm->GetYesNode(), NULL, 0 );
706  Order *o = leader->getAIState();
707  if (o)
708  o->Communicate( c );
709  static float esc_percent = XMLSupport::parse_float( vs_config->getVariable( "AI",
710  "Targetting",
711  "EscortDistance",
712  "10.0" ) );
713  static float turn_leader = XMLSupport::parse_float( vs_config->getVariable( "AI",
714  "Targetting",
715  "TurnLeaderDist",
716  "5.0" ) );
717  int fgnum = parent->getFgSubnumber();
718  if ( parent->getFlightgroup() ) {
719  int tempnum = 0;
720  string nam = parent->getFlightgroup()->name;
721  int i = nam.length()-1;
722  for (; i >= 0; --i) {
723  char digit = nam[i];
724  if (digit >= '0' && digit <= '9') {
725  tempnum *= 10;
726  tempnum += digit-'0';
727  } else {
728  break;
729  }
730  }
731  fgnum += tempnum;
732  }
733  float left = fgnum%2 ? 1 : -1;
734 
735  double dist = esc_percent*(1+abs( fgnum-1 )/2)*left*( parent->rSize()+leader->rSize() );
736  Order *ord = new Orders::FormUp( QVector( dist, 0, -fabs( dist ) ) );
737  ord->SetParent( parent );
738  ReplaceOrder( ord );
739  ord = new Orders::FaceDirection( dist*turn_leader );
740  ord->SetParent( parent );
741  ReplaceOrder( ord );
742  }
743  } else {
744  last_time_insys = false;
745  }
746  for (unsigned int i = 0; i < suborders.size(); i++)
747  suborders[i]->AttachSelfOrder( leader );
748  }
749  }
750  //IAmDave - hold position command
751  else if (fg->directive.find( "s" ) != string::npos || fg->directive.find( "S" ) != string::npos) {
752  Order * ord = new Orders::MatchVelocity(Vector(0,0,0),Vector(0,0,0),true,false);
753  ord->SetParent( parent );
754  ReplaceOrder( ord );
755  }
756  //IAmDave - dock at target command start...
757  else if (fg->directive.find( "t" ) != string::npos || fg->directive.find( "T" ) != string::npos) {
758  Unit *targ = fg->target.GetUnit();
759  bool callme = false;
760  if ( targ->InCorrectStarSystem( _Universe->activeStarSystem() ) ) {
761  Order * ord;
762  if(targ->IsBase()){
763  ord = new Orders::DockingOps(targ,new Orders::MatchVelocity(Vector(0,0,0),Vector(0,0,0),true,false),true,true);
764  }
765  else{
766  ord = new Orders::MoveTo(targ->Position(),true,4);
767  }
768  ord->SetParent( parent );
769  ReplaceOrder( ord );
770  }
771 
772 
773  }
774  //IAmDave - ...dock at target command end.
775  else if (fg->directive.find( "l" ) != string::npos || fg->directive.find( "L" ) != string::npos) {
776  if (leader != NULL) {
777  if ( leader->InCorrectStarSystem( _Universe->activeStarSystem() ) ) {
778  retval = true;
779  if ( fg->directive != last_directive || (!last_time_insys) ) {
780  last_time_insys = true;
781  CommunicationMessage c( parent, leader, NULL, 0 );
782 //this order is only valid for cargo wingmen, other wingmen will not comply
783  c.SetCurrentState( c.fsm->GetYesNode(), NULL, 0 );
784  Order *o = leader->getAIState();
785  if (o)
786  o->Communicate( c );
787  static float esc_percent = XMLSupport::parse_float( vs_config->getVariable( "AI",
788  "Targetting",
789  "EscortDistance",
790  "10.0" ) );
791  static float turn_leader = XMLSupport::parse_float( vs_config->getVariable( "AI",
792  "Targetting",
793  "TurnLeaderDist",
794  "5.0" ) );
795  int fgnum = parent->getFgSubnumber();
796  if ( parent->getFlightgroup() ) {
797  int tempnum = 0;
798  string nam = parent->getFlightgroup()->name;
799  int i = nam.length()-1;
800  for (; i >= 0; --i) {
801  char digit = nam[i];
802  if (digit >= '0' && digit <= '9') {
803  tempnum *= 10;
804  tempnum += digit-'0';
805  } else {
806  break;
807  }
808  }
809  fgnum += tempnum;
810  }
811 /*
812  * // this does the job for real! "parent" is executor, "leader" is commander
813  *
814  * // moves where you want it to
815  * // moves flat out in front of parent unit (to allow for tractoring)
816  * Order * ord = new Orders::FormUp(QVector(position*parent->radial_size,0,fabs(dist)));
817  * ord->SetParent (parent);
818  * ReplaceOrder (ord);
819  * // faces same direction as leader
820  * // ord = new Orders::FaceDirection(dist*turn_leader);
821  * // faces opposite direction as leader, as in, stare at me in the face please
822  * ord = new Orders::FaceDirection(-dist*turn_leader);
823  * ord->SetParent (parent);
824  * ReplaceOrder (ord);
825  */
826 
827  int alternate = fgnum%2 ? 1 : -1;
828  float psize = parent->radial_size;
829  int Ypos = 0;
830  int Xpos = 0;
831 //nice square formation, how many of these are you going to have anyway? Max 9, then go back. Should be enough.
832  switch (fgnum%9)
833  {
834  case 0:
835  Xpos = 0;
836  Ypos = 0;
837  break;
838  case 1:
839  Xpos = -1;
840  Ypos = 0;
841  break;
842  case 2:
843  Xpos = 1;
844  Ypos = 0;
845  break;
846  case 3:
847  Xpos = 0;
848  Ypos = -1;
849  break;
850  case 4:
851  Xpos = -1;
852  Ypos = -1;
853  break;
854  case 5:
855  Xpos = 1;
856  Ypos = -1;
857  break;
858  case 6:
859  Xpos = 0;
860  Ypos = 1;
861  break;
862  case 7:
863  Xpos = -1;
864  Ypos = 1;
865  break;
866  case 8:
867  Xpos = 1;
868  Ypos = 1;
869  break;
870  default:
871  Xpos = 0;
872  Ypos = 0;
873  }
874  float dist = (leader->radial_size+parent->radial_size*2);
875  float formdist = esc_percent*(1+fgnum*2)*alternate*(dist);
876  //if i am a cargo wingman, get into a dockable position
877  if (parentowner == leader) {
878  // move in front
879  Order *ord = new Orders::FormUp( QVector( 0, 0, fabs( dist ) ) );
880  ord->SetParent( parent );
881  ReplaceOrder( ord );
882  //facing me
883  ord = new Orders::FaceDirection( -dist*turn_leader );
884  EnqueueOrderFirst( ord );
885  }
886  //if i am a cargo wingman and so is the player, get into a dockable position with the leader
887  else if ( parentowner && leaderowner && (parentowner == leaderowner) ) {
888  Unit *leaderownerun =
889  ( leaderowner
890  == leader ? leader : ( leaderowner == parent ? parent : findUnitInStarsystem( leaderowner ) ) );
891  float qdist = ( parent->rSize()+leaderownerun->rSize() );
892  Order *ord =
893  new Orders::MoveTo( leaderownerun->Position()+Vector( 0.5*Xpos*psize,
894  0.5*Ypos*psize,
895  0.5*qdist ), true, 4 );
896  ord->SetParent( parent );
897  ReplaceOrder( ord );
898  //facing it
899  ord = new Orders::FaceDirection( -qdist*turn_leader );
900  ord->SetParent( parent );
901  ReplaceOrder( ord );
902  }
903  //if i am the capship, go into defensive mode
904  else if (parent == leaderowner) {
905  parent->SetTurretAI();
906  TurretFAW( parent );
908  0,
909  0 ),
910  true ), true, false, true );
911  ord->SetParent( parent );
912  ReplaceOrder( ord );
913  if (parent->Target() != NULL)
914  ord = new Orders::FaceTarget( false, 3 );
915  else
916  ord = new Orders::FaceDirection( -dist*turn_leader );
917  ord->SetParent( parent );
918  ReplaceOrder( ord );
919  } else {
920  //if i'm not a cargo wingman, just form up somewhat loosely.
921  parentowner = parent;
922  Order *ord =
923  new Orders::FormUp( QVector( 5*Xpos*psize, 5*Ypos*psize, -fabs( formdist )+Ypos*psize+Xpos
924  *psize ) );
925  ord->SetParent( parent );
926  ReplaceOrder( ord );
927  ord = new Orders::FaceDirection( dist*turn_leader );
928  ord->SetParent( parent );
929  ReplaceOrder( ord );
930  }
931  }
932  } else {
933  last_time_insys = false;
934  }
935  for (unsigned int i = 0; i < suborders.size(); i++)
936  suborders[i]->AttachSelfOrder( leader );
937  }
938  } else if (fg->directive.find( "e" ) != string::npos || fg->directive.find( "E" ) != string::npos) {
939  static QVector LeaderPosition = QVector( 0, 0, 0 );
940  if (LeaderPosition.Magnitude() > 0 || leader != NULL) {
941  if ( LeaderPosition.Magnitude() > 0 || leader->InCorrectStarSystem( _Universe->activeStarSystem() ) ) {
942  retval = true;
943  if (LeaderPosition.Magnitude() == 0) //only read the position the first time
944  LeaderPosition = leader->Position();
945  if ( fg->directive != last_directive || (!last_time_insys) ) {
946  last_time_insys = true;
947  CommunicationMessage c( parent, leader, NULL, 0 );
948 //this order is only valid for cargo wingmen, other wingmen will not comply
949  c.SetCurrentState( c.fsm->GetYesNode(), NULL, 0 );
950  static float turn_leader = XMLSupport::parse_float( vs_config->getVariable( "AI",
951  "Targetting",
952  "TurnLeaderDist",
953  "5.0" ) );
954  int fgnum = parent->getFgSubnumber();
955  if ( parent->getFlightgroup() ) {
956  int tempnum = 0;
957  string nam = parent->getFlightgroup()->name;
958  int i = nam.length()-1;
959  for (; i >= 0; --i) {
960  char digit = nam[i];
961  if (digit >= '0' && digit <= '9') {
962  tempnum *= 10;
963  tempnum += digit-'0';
964  } else {
965  break;
966  }
967  }
968  fgnum += tempnum;
969  }
970 /*
971  * // this does the job for real! "parent" is executor, "leader" is commander
972  *
973  * // moves where you want it to
974  * // moves flat out in front of parent unit (to allow for tractoring)
975  * Order * ord = new Orders::FormUp(QVector(position*parent->radial_size,0,fabs(dist)));
976  * ord->SetParent (parent);
977  * ReplaceOrder (ord);
978  * // faces same direction as leader
979  * // ord = new Orders::FaceDirection(dist*turn_leader);
980  * // faces opposite direction as leader, as in, stare at me in the face please
981  * ord = new Orders::FaceDirection(-dist*turn_leader);
982  * ord->SetParent (parent);
983  * ReplaceOrder (ord);
984  */
985 
986  float psize = parent->radial_size;
987  int Ypos = 0;
988  int Xpos = 0;
989 //nice square formation, how many of these are you going to have anyway? Max 9, then go back. Should be enough.
990  switch (fgnum%9)
991  {
992  case 0:
993  Xpos = 0;
994  Ypos = 0;
995  break;
996  case 1:
997  Xpos = -1;
998  Ypos = 0;
999  break;
1000  case 2:
1001  Xpos = 1;
1002  Ypos = 0;
1003  break;
1004  case 3:
1005  Xpos = 0;
1006  Ypos = -1;
1007  break;
1008  case 4:
1009  Xpos = -1;
1010  Ypos = -1;
1011  break;
1012  case 5:
1013  Xpos = 1;
1014  Ypos = -1;
1015  break;
1016  case 6:
1017  Xpos = 0;
1018  Ypos = 1;
1019  break;
1020  case 7:
1021  Xpos = -1;
1022  Ypos = 1;
1023  break;
1024  case 8:
1025  Xpos = 1;
1026  Ypos = 1;
1027  break;
1028  default:
1029  Xpos = 0;
1030  Ypos = 0;
1031  }
1032  //if i am a cargo wingman go close for pickup
1033  //if i am the capship, go close for pickup
1034  if ( (parent->owner == leader->owner) || parent->owner == leader ) {
1035 //float left= fgnum%2?1:-1;
1036  float qdist = ( 1.5*parent->rSize()+1.5*leader->rSize() );
1037  Order *ord =
1038  new Orders::MoveTo( LeaderPosition+Vector( 0.5*Xpos*psize,
1039  0.5*Ypos*psize,
1040  0.5*qdist ), true, 4 );
1041  ord->SetParent( parent );
1042  ReplaceOrder( ord );
1043  //facing it
1044  ord = new Orders::FaceDirection( -qdist*turn_leader );
1045  ord->SetParent( parent );
1046  ReplaceOrder( ord );
1047  }
1048  //if i'm not a cargo wingman, IT'S NOT MY PROBLEM.
1049  else {
1050  parent->owner = parent;
1051  c.SetCurrentState( c.fsm->GetNoNode(), NULL, 0 );
1052  Flightgroup *leave = new Flightgroup();
1053  leave->directive = "b";
1054  parent->SetFg( leave, 1 );
1055  }
1056  Order *o = leader->getAIState();
1057  if (o)
1058  o->Communicate( c );
1059  }
1060  } else {
1061  last_time_insys = false;
1062  }
1063  for (unsigned int i = 0; i < suborders.size(); i++)
1064  suborders[i]->AttachSelfOrder( leader );
1065  }
1066  } else if (fg->directive.find( "h" ) != string::npos || fg->directive.find( "H" ) != string::npos) {
1067  if (fg->directive != last_directive && leader) {
1068  if ( leader->InCorrectStarSystem( _Universe->activeStarSystem() ) ) {
1069  Unit *th = NULL;
1070  if ( ( th = leader->Threat() ) ) {
1071  CommunicationMessage c( parent, leader, NULL, 0 );
1072  if ( parent->InRange( th, true, false ) ) {
1073  parent->Target( th );
1074  parent->TargetTurret( th );
1075  c.SetCurrentState( c.fsm->GetYesNode(), NULL, 0 );
1076  } else {
1077  c.SetCurrentState( c.fsm->GetNoNode(), NULL, 0 );
1078  }
1079  Order *oo = leader->getAIState();
1080  if (oo)
1081  oo->Communicate( c );
1082  } else {
1083  th = GetThreat( parent, leader );
1084  CommunicationMessage c( parent, leader, NULL, 0 );
1085  if (th) {
1086  if ( parent->InRange( th, true, false ) ) {
1087  c.SetCurrentState( c.fsm->GetYesNode(), NULL, 0 );
1088  parent->Target( th );
1089  parent->TargetTurret( th );
1090 //if I am the capship, go into defensive mode.
1091  if (parent == leader->owner) {
1092  //parent->Target(parent);
1093  parent->SetTurretAI();
1094  TurretFAW( parent );
1095  Order *ord =
1097  0,
1098  0 ),
1099  true ), true, false, true );
1100  ord->SetParent( parent );
1101  ReplaceOrder( ord );
1102  if (parent->Target() != NULL) {
1103  ord = new Orders::FaceTarget( false, 3 );
1104  ord->SetParent( parent );
1105  ReplaceOrder( ord );
1106  }
1107  }
1108  } else {}
1109  } else {
1110  c.SetCurrentState( c.fsm->GetNoNode(), NULL, 0 );
1111  }
1112  Order *loo = leader->getAIState();
1113  if (loo)
1114  loo->Communicate( c );
1115  }
1116  }
1117  }
1118  } else if (fg->directive.find( "p" ) != string::npos || fg->directive.find( "P" ) != string::npos) {
1119  bool callme = false;
1120  if (fg->directive != last_directive && leader) {
1121  if ( leader->InCorrectStarSystem( _Universe->activeStarSystem() ) ) {
1122  Unit *th = NULL;
1123  Unit *targ = fg->target.GetUnit();
1124  if ( targ && ( th = targ->Threat() ) ) {
1125  CommunicationMessage c( parent, leader, NULL, 0 );
1126  if ( parent->InRange( th, true, false ) ) {
1127  parent->Target( th );
1128  parent->TargetTurret( th );
1129  c.SetCurrentState( c.fsm->GetYesNode(), NULL, 0 );
1130  fg->directive = "";
1131  } else {
1132  c.SetCurrentState( c.fsm->GetNoNode(), NULL, 0 );
1133  }
1134  Order *oo = leader->getAIState();
1135  if (oo)
1136  oo->Communicate( c );
1137  } else {
1138  th = GetThreat( parent, targ );
1139  CommunicationMessage c( parent, leader, NULL, 0 );
1140  if (th) {
1141  if ( parent->InRange( th, true, false ) ) {
1142  c.SetCurrentState( c.fsm->GetYesNode(), NULL, 0 );
1143  if ( th != parent->Target() )
1144  callme = true;
1145  parent->Target( th );
1146  parent->TargetTurret( th );
1147 //if I am the capship, go into defensive mode.
1148  if (parent == leaderowner) {
1149  parent->SetTurretAI();
1150  parent->TurretFAW();
1151  Order *ord =
1153  0,
1154  0 ),
1155  true ), true, false, true );
1156  ord->SetParent( parent );
1157  ReplaceOrder( ord );
1158  if (parent->Target() != NULL) {
1159  ord = new Orders::FaceTarget( false, 3 );
1160  ord->SetParent( parent );
1161  ReplaceOrder( ord );
1162  }
1163  }
1164  } else {
1165  c.SetCurrentState( c.fsm->GetNoNode(), NULL, 0 );
1166  }
1167  } else {
1168  c.SetCurrentState( c.fsm->GetNoNode(), NULL, 0 );
1169  }
1170  Order *loo = leader->getAIState();
1171  if (loo)
1172  loo->Communicate( c );
1173  }
1174  }
1175  }
1176  }
1177  }
1178  last_directive = fg->directive;
1179  }
1180  return retval;
1181 }
1182 
1183 static bool overridable( const std::string &s )
1184 {
1185  if ( s.empty() )
1186  return true;
1187  return ( *s.begin() ) != toupper( *s.begin() );
1188 }
1189 
1190 extern void LeadMe( Unit *un, string directive, string speech, bool changetarget );
1191 
1193 {
1194  static float time_to_recommand_wing = XMLSupport::parse_float( vs_config->getVariable( "AI",
1195  "Targetting",
1196  "TargetCommandierTime",
1197  "100" ) );
1198  static bool verbose_debug = XMLSupport::parse_bool( vs_config->getVariable( "data", "verbose_debug", "false" ) );
1199  if (fg != NULL) {
1200  Unit *lead;
1201  if ( overridable( fg->directive ) ) {
1202  //computer won't override capital orders
1203  if ( NULL != ( lead = fg->leader.GetUnit() ) ) {
1204  if (float( rand() )/RAND_MAX < SIMULATION_ATOM/time_to_recommand_wing) {
1205  if ( parent->Threat() && (parent->FShieldData() < .2 || parent->RShieldData() < .2) ) {
1206  fg->directive = string( "h" );
1207  LeadMe( parent, "h", "I need help here!", false );
1208  if (verbose_debug)
1209  VSFileSystem::vs_fprintf( stderr, "he needs help %s", parent->name.get().c_str() );
1210  } else if ( lead->getFgSubnumber() >= parent->getFgSubnumber() ) {
1211  fg->directive = string( "b" );
1212  LeadMe( parent, "b", "I'm taking over this wing. Break and attack", false );
1213  }
1214  }
1215  }
1216  }
1217  }
1218 }
1219 
1220 static Unit * GetRandomNav( vector< UnitContainer >navs[3], unsigned int randnum )
1221 {
1222  size_t total_size = navs[0].size()+navs[1].size()+navs[2].size();
1223  if (total_size == 0) return NULL;
1224  randnum %= total_size;
1225  if ( randnum >= navs[0].size() ) {
1226  randnum -= navs[0].size();
1227  if ( randnum >= navs[1].size() ) {
1228  randnum -= navs[1].size();
1229  return navs[2][randnum].GetUnit();
1230  }
1231  return navs[1][randnum].GetUnit();
1232  }
1233  return navs[0][randnum].GetUnit();
1234 }
1235 
1236 static std::string insysString( "Insys" );
1237 
1238 static std::string arrowString( "->" );
1239 
1240 static Unit * ChooseNavPoint( Unit *parent, Unit **otherdest, float *lurk_on_arrival )
1241 {
1242  static string script = vs_config->getVariable( "AI", "ChooseDestinationScript", "" );
1243  *lurk_on_arrival = 0;
1244  if (script.length() > 0) {
1245  Unit *ret = NULL;
1246  UniverseUtil::setScratchUnit( parent );
1247  CompileRunPython( script );
1250  if (ret != NULL && ret != parent)
1251  return ret;
1252  }
1254  StarSystem::Statistics *stats = &ss->stats;
1255 
1256  float sysrel = UnitUtil::getRelationFromFaction( parent, stats->system_faction );
1257  static float lurk_time = XMLSupport::parse_float( vs_config->getVariable( "AI", "lurk_time", "600" ) );
1258  static float select_time = XMLSupport::parse_float( vs_config->getVariable( "AI", "fg_nav_select_time", "120" ) );
1259  static float hostile_select_time = XMLSupport::parse_float( vs_config->getVariable( "AI", "pirate_nav_select_time", "400" ) );
1260  static int num_ships_per_roid =
1261  XMLSupport::parse_int( vs_config->getVariable( "AI", "num_pirates_per_asteroid_field", "12" ) );
1262  bool civilian = FactionUtil::isCitizenInt( parent->faction );
1263 
1264  bool hostile = sysrel < 0;
1265  bool anarchy = stats->enemycount > stats->friendlycount;
1266  float timehash = select_time;
1267  if (hostile && !anarchy)
1268  timehash = hostile_select_time;
1269  unsigned int firstRand, thirdRand;
1270  float secondRand;
1271  const unsigned int maxrand = 5;
1272  unsigned int additionalrand[maxrand];
1273  if (civilian) {
1274  firstRand = vsrandom.genrand_int31();
1275  secondRand = vsrandom.uniformExc( 0, 1 );
1276  thirdRand = vsrandom.genrand_int31();
1277  for (unsigned int i = 0; i < maxrand; ++i)
1278  additionalrand[i] = thirdRand+i;
1279  } else {
1280  int k = (int) (getNewTime()/timehash); //two minutes
1281  string key = UnitUtil::getFlightgroupName( parent );
1282  std::string::const_iterator start = key.begin();
1283  for (; start != key.end(); start++)
1284  k += (k*128)+*start;
1285  VSRandom choosePlace( k );
1286  firstRand = choosePlace.genrand_int31();
1287  secondRand = choosePlace.uniformExc( 0, 1 );
1288  thirdRand = choosePlace.genrand_int31();
1289  for (unsigned int i = 0; i < maxrand; ++i)
1290  additionalrand[i] = choosePlace.genrand_int31();
1291  }
1292  bool asteroidhide = false;
1293  if (stats->friendlycount > 0 && stats->enemycount > 0)
1294  asteroidhide = (secondRand < stats->enemycount/(float) stats->friendlycount)
1295  && (secondRand < num_ships_per_roid*stats->navs[2].size()/(float) stats->enemycount);
1296  bool siege = stats->enemycount > 2*stats->friendlycount; //rough approx
1297  int whichlist = 1; //friendly
1298  std::string fgname = UnitUtil::getFlightgroupName( parent );
1299 
1300  bool insys = (parent->GetJumpStatus().drive == -2) || fgname.find( insysString ) != std::string::npos;
1301  std::string::size_type whereconvoy = fgname.find( arrowString );
1302  bool convoy = (whereconvoy != std::string::npos);
1303  size_t total_size = stats->navs[0].size()+stats->navs[whichlist].size(); //friendly and neutral
1304  static bool bad_units_lurk = XMLSupport::parse_bool( vs_config->getVariable( "AI", "hostile_lurk", "true" ) );
1305  if (hostile && bad_units_lurk) {
1306  if (anarchy && !siege) {
1307  whichlist = 2;
1308  total_size = stats->navs[0].size()+stats->navs[whichlist].size(); //asteroids and neutrals
1309  } else {
1310  whichlist = 2;
1311  total_size = stats->navs[whichlist].size(); //just asteroids
1312  }
1313  } else if (civilian) {
1314  if (anarchy || siege) {
1315  whichlist = 0;
1316  total_size = stats->navs[0].size();
1317  } else if (insys || convoy) {
1318  whichlist = 1;
1319  total_size = stats->navs[1].size(); //don't go to jump point
1320  }
1321  }
1322  if (hostile && ( (anarchy == false && asteroidhide == false) || total_size == 0 ) && civilian == false && bad_units_lurk) {
1323  //hit and run
1324  Unit *a = GetRandomNav( stats->navs, firstRand );
1325  Unit *b = GetRandomNav( stats->navs, thirdRand );
1326  if (a == b)
1327  b = GetRandomNav( stats->navs, thirdRand+1 );
1328  if (a != b) {
1329  int retrycount = maxrand;
1330  while ( --retrycount > 0 && (UnitUtil::getDistance( a, b ) < parent->GetComputerData().radar.maxrange*4 || a == b) )
1331  b = GetRandomNav( stats->navs, additionalrand[retrycount] );
1332  if (retrycount != 0) {
1333  *otherdest = b;
1334  *lurk_on_arrival = lurk_time;
1335  }
1336  }
1337  return a;
1338  } else {
1339  if (convoy) {
1340  std::string srcdst[2] = {fgname.substr( 0, whereconvoy ), fgname.substr( whereconvoy+2 )};
1341  if ( srcdst[0] == ss->getFileName() )
1342  srcdst[0] = srcdst[1];
1343  if ( srcdst[1] == ss->getFileName() )
1344  srcdst[1] = srcdst[0];
1345  if (thirdRand < 2) {
1346  vsUMap< std::string, UnitContainer >::iterator i = stats->jumpPoints.find( srcdst[thirdRand] );
1347  if ( i != stats->jumpPoints.end() ) {
1348  Unit *un = i->second.GetUnit();
1349  if (un)
1350  return un;
1351  } else {
1352  total_size = stats->navs[whichlist].size()+stats->navs[0].size(); //no such jump point--have to random-walk it
1353  //maybe one day we can incorporate some sort of route planning
1354  }
1355  }
1356  }
1357  if (total_size > 0) {
1358  firstRand %= total_size;
1359  if ( firstRand >= stats->navs[whichlist].size() ) {
1360  firstRand -= stats->navs[whichlist].size();
1361  whichlist = 0; //allows you to look for both neutral and ally lists
1362  }
1363  return stats->navs[whichlist][firstRand].GetUnit();
1364  }
1365  }
1366  return NULL;
1367 }
1368 
1369 static Unit * ChooseNearNavPoint( Unit *parent, Unit *suggestion, QVector location, float locradius )
1370 {
1371  if (suggestion) return suggestion;
1372  Unit *candidate = NULL;
1373  float dist = FLT_MAX;
1374  Unit *un;
1377  parent->location[Unit::UNIT_ONLY],
1378  &nnl );
1379  return nnl.retval.unit;
1380  //DEAD CODE
1382  (un = *i) != NULL;
1383  ++i)
1384  if (UnitUtil::isSignificant( un ) && un != parent) {
1385  float newdist = ( location-un->Position() ).Magnitude()-un->rSize()-locradius;
1386  if (candidate == NULL || newdist <= dist) {
1387  candidate = un;
1388  dist = newdist;
1389  }
1390  }
1391  return candidate;
1392  //END DEAD CODE
1393 }
1394 
1395 bool CloseEnoughToNavOrDest( Unit *parent, Unit *navUnit, QVector nav )
1396 {
1397  static float how_far_to_stop_moving =
1398  XMLSupport::parse_float( vs_config->getVariable( "AI", "how_far_to_stop_navigating", "100" ) );
1399  if (navUnit && navUnit->isUnit() != PLANETPTR) {
1400  float dist = UnitUtil::getDistance( navUnit, parent );
1401  if (dist < SIMULATION_ATOM*parent->Velocity.Magnitude()*parent->predicted_priority*how_far_to_stop_moving)
1402  return true;
1403  }
1404  return ( nav-parent->Position() ).MagnitudeSquared() < 4*parent->rSize()*parent->rSize();
1405 }
1406 
1407 volatile Unit *uoif;
1408 
1409 class FlyTo : public Orders::MoveTo
1410 {
1411  float creationtime;
1412  UnitContainer destUnit;
1413 public: FlyTo( const QVector &target,
1414  bool aft,
1415  bool terminating = true,
1416  float creationtime = 0,
1417  int leniency = 6,
1418  Unit *destUnit = NULL ) : MoveTo( target, aft, leniency, terminating )
1419  {
1420  this->creationtime = creationtime;
1421  this->destUnit = destUnit;
1422  }
1423 
1424  virtual void Execute()
1425  {
1426  if (parent == uoif)
1427  printf( "kewl" );
1428  MoveTo::Execute();
1429  Unit *un = destUnit.GetUnit();
1430  if ( CloseEnoughToNavOrDest( parent, un, targetlocation ) )
1431  done = true;
1432  un = NULL;
1433  static float mintime = XMLSupport::parse_float( vs_config->getVariable( "AI", "min_time_to_auto", "25" ) );
1434  if (getNewTime()-creationtime > mintime) {
1436  && ( un = ChooseNearNavPoint( parent, destUnit.GetUnit(), targetlocation, 0 ) ) != NULL) {
1437  WarpToP( parent, un, true );
1438  } else {
1439  Unit *playa = _Universe->AccessCockpit()->GetParent();
1440  if (playa == NULL || playa->Target() != parent || 1)
1441  WarpToP( parent, targetlocation, 0, true );
1442  }
1443  }
1444  }
1445 };
1446 
1448 {
1449  return Vector( (rand()/(float) RAND_MAX)*2-1, (rand()/(float) RAND_MAX)*2-1, (rand()/(float) RAND_MAX)*2-1 );
1450 }
1451 
1452 static void GoTo( AggressiveAI *ai,
1453  Unit *parent,
1454  const QVector &nav,
1455  float creationtime,
1456  bool boonies = false,
1457  Unit *destUnit = NULL )
1458 {
1459  static bool can_afterburn = XMLSupport::parse_bool( vs_config->getVariable( "AI", "afterburn_to_no_enemies", "true" ) );
1460  Order *mt = new FlyTo( nav, can_afterburn, true, creationtime, boonies ? 16 : 6, destUnit );
1461  Order *ch = new Orders::ChangeHeading( nav, 32, .25f, false );
1462  ai->eraseType( Order::FACING );
1463  ai->eraseType( Order::MOVEMENT );
1464  mt->SetParent( parent );
1465  ch->SetParent( parent );
1466  ai->ReplaceOrder( mt );
1467  ai->EnqueueOrder( ch );
1468 }
1469 
1471 {
1472  static float safetyspacing = XMLSupport::parse_float( vs_config->getVariable( "AI", "safetyspacing", "2500" ) );
1473  static float randspacingfactor = XMLSupport::parse_float( vs_config->getVariable( "AI", "randomspacingfactor", "4" ) );
1474  if (nav.i == 0 && nav.j == 0 && nav.k == 0) {
1475  Unit *otherdest = NULL;
1476  Unit *dest = ChooseNavPoint( parent, &otherdest, &this->lurk_on_arrival );
1477  if (dest) {
1478  static bool can_warp_to = XMLSupport::parse_bool( vs_config->getVariable( "AI", "warp_to_no_enemies", "true" ) );
1479  static float mintime = XMLSupport::parse_float( vs_config->getVariable( "AI", "min_time_to_auto", "25" ) );
1480  if (getNewTime()-creationtime > mintime) {
1481  if (can_warp_to)
1482  WarpToP( parent, dest, true );
1484  WarpToP( parent, dest, true );
1485  }
1486  Vector dir = parent->Position()-dest->Position();
1487  Vector unitdir = dir.Normalize();
1488  if (!otherdest) {
1489  navDestination = dest;
1490  dir = unitdir*( dest->rSize()+parent->rSize() );
1491  if (dest->isUnit() == PLANETPTR) {
1492  float planetpct = UniverseUtil::getPlanetRadiusPercent();
1493  dir *= (planetpct+1.0f);
1494  dir += randVector()*parent->rSize()*2*randspacingfactor;
1495  } else {
1496  dir *= 2;
1497  dir += (unitdir*safetyspacing);
1498  dir +=
1499  ( (randVector()*randspacingfactor
1500  /4)
1501  +(unitdir
1502  *randspacingfactor) )
1503  *( ( parent->rSize() > (safetyspacing/5) ) ? (safetyspacing/5) : ( parent->rSize() ) );
1504  }
1505  }
1506  nav = dest->Position()+dir;
1507  if (otherdest) {
1508  nav += otherdest->Position();
1509  nav = nav*.5;
1510  }
1511 #ifdef AGGDEBUG
1512  std::string fgname = UnitUtil::getFlightgroupName( parent );
1513  std::string pfullname = parent->getFullname();
1514  std::string dfullname = dest->getFullname();
1515  printf( "%s:%s %s going to %s:%s", parent->name.get().c_str(), pfullname.c_str(), fgname.c_str(),
1516  dest->name.get().c_str(), dfullname.c_str() );
1517  if (otherdest) {
1518  std::string ofullname = otherdest->getFullname();
1519  printf( " between %s:%s\n", otherdest->name.get().c_str(), ofullname.c_str() );
1520  } else {printf( "\n" ); }
1521 #endif
1522  GoTo( this, parent, nav, creationtime, otherdest != NULL, otherdest == NULL ? dest : NULL );
1523  }
1524  } else {
1526  std::string fgname = UnitUtil::getFlightgroupName( parent );
1527 
1528  nav = QVector( 0, 0, 0 );
1530  if (dest) {
1531  if ( fgname.find( insysString ) == string::npos && dest->GetDestinations().size() > 0
1532  && UniverseUtil::systemInMemory( dest->GetDestinations()[0] ) ) {
1533  parent->ActivateJumpDrive( 0 );
1534  parent->Target( dest ); //fly there, baby!
1535  } else if ( dest->GetDestinations().size() == 0 && false == UnitUtil::isCapitalShip( parent )
1536  && UnitUtil::isDockableUnit( dest ) ) {
1537  Order *ai = parent->aistate;
1538  parent->aistate = NULL;
1539  parent->PrimeOrders( new Orders::DockingOps( dest, ai, true, false ) );
1540  } else {
1541  ExecuteNoEnemies(); //find a new place to go
1542  }
1543  } else {
1544  ExecuteNoEnemies(); //no suitable docking point found, recursive call which will take door1
1545  //go dock to the nav point
1546  }
1547  } else if (lurk_on_arrival > 0) {
1549  //slowdown
1552  if (lurk_on_arrival <= 0) {
1553  nav = QVector( 0, 0, 0 );
1554  ExecuteNoEnemies(); //select new place to go
1555  }
1556  //have to do something while here.
1557  } else {
1558  GoTo( this, parent, nav, creationtime, false, navDestination.GetUnit() );
1559  }
1560  }
1561 }
1562 
1564 {
1565  AfterburnTurnTowards( this, parent );
1566  static float jump_time_limit = XMLSupport::parse_float( vs_config->getVariable( "AI", "force_jump_after_time", "120" ) );
1567  if (jump_time_check == 0) {
1568  float dist = ( target->Position()-parent->Position() ).MagnitudeSquared();
1569  if (last_jump_distance < dist || last_jump_time > jump_time_limit) {
1570  //force jump
1571  last_jump_time = 0;
1572  if ( target->GetDestinations().size() ) {
1573  string dest = target->GetDestinations()[0];
1574  UnitUtil::JumpTo( parent, dest );
1575  }
1576  } else {
1577  last_jump_distance = dist;
1578  }
1579  }
1580 }
1581 
1582 volatile Unit *uoi;
1583 
1585 {
1586  if (parent == uoi)
1587  printf( "kewl" );
1588  jump_time_check++; //just so we get a nicely often wrapping var;
1589  jump_time_check %= 5;
1591  double firetime = queryTime();
1592  static int pir = FactionUtil::GetFactionIndex( "pirates" );
1593  if (parent->faction == pir)
1594  if (rand() == 0) printf( "ahoy, a pirates!" );
1595  FireAt::Execute();
1596  aggfire += queryTime()-firetime;
1597  static bool resistance_to_side_movement =
1598  XMLSupport::parse_bool( vs_config->getVariable( "AI", "resistance_to_side_movement", "false" ) );
1599  if (resistance_to_side_movement) {
1600  Vector p, q, r;
1601  parent->GetOrientation( p, q, r );
1602  float forwardness = parent->Velocity.Dot( r );
1603  Vector countervelocity = -parent->Velocity;
1604  Vector counterforce = -parent->NetForce;
1605  float forceforwardness = parent->NetForce.Dot( r );
1606  if (forceforwardness > 0)
1607  counterforce = forceforwardness*r-parent->NetForce;
1608  if (forwardness > 0)
1609  countervelocity = forwardness*r-parent->Velocity;
1610  static float resistance_percent =
1611  XMLSupport::parse_float( vs_config->getVariable( "AI", "resistance_to_side_movement_percent", ".01" ) );
1612  static float force_resistance_percent =
1613  XMLSupport::parse_float( vs_config->getVariable( "AI", "resistance_to_side_force_percent", "1" ) );
1614  parent->Velocity += countervelocity*resistance_percent;
1615  parent->NetForce += counterforce*force_resistance_percent;
1616  counterforce = -parent->NetLocalForce;
1617  counterforce.k = 0;
1618  parent->NetLocalForce += counterforce*force_resistance_percent;
1619  }
1620  Unit *target = parent->Target();
1621 
1622  bool isjumpable = target ? ( !target->GetDestinations().empty() ) : false;
1623  if ( !ProcessCurrentFgDirective( fg ) ) {
1624  if (isjumpable) {
1625  if (parent->GetJumpStatus().drive < 0) {
1626  parent->ActivateJumpDrive( 0 );
1627  if (parent->GetJumpStatus().drive == -2) {
1628  static bool AIjumpCheat =
1629  XMLSupport::parse_bool( vs_config->getVariable( "AI", "always_have_jumpdrive_cheat", "false" ) );
1630  if (AIjumpCheat) {
1631  static int i = 0;
1632  if (!i) {
1633  VSFileSystem::vs_fprintf( stderr, "FIXME: warning ship not equipped to jump" );
1634  i = 1;
1635  }
1636  parent->jump.drive = -1;
1637  } else {
1638  parent->Target( NULL );
1639  }
1640  } else if (parent->GetJumpStatus().drive < 0) {
1641  static bool AIjumpCheat = XMLSupport::parse_bool( vs_config->getVariable( "AI", "jump_cheat", "true" ) );
1642  if (AIjumpCheat)
1643  parent->jump.drive = 0;
1644  }
1645  }
1646  last_jump_time += SIMULATION_ATOM;
1647  } else {
1648  last_jump_time = 0;
1649  }
1650  if ( (!isjumpable) && interruptcurtime <= 0 && target )
1651  ProcessLogic( *logic, true );
1652  if (!target) {
1654  if (logiccurtime < 0) {
1655  logiccurtime = 20;
1656  currentpriority = -FLT_MAX;
1657  }
1658  }
1659  if (queryAny( Order::FACING|Order::MOVEMENT ) == NULL) {
1660  if (isjumpable) {
1661  AfterburnerJumpTurnTowards( target );
1662  } else {
1663  last_jump_distance = FLT_MAX;
1664  if (target) {
1665  ProcessLogic( *logic, false );
1666  } else {}
1667  }
1668  if (!isjumpable)
1669  ExecuteNoEnemies();
1670  } else {
1671  if (target) {
1672  static bool can_warp_to = XMLSupport::parse_bool( vs_config->getVariable( "AI", "warp_to_enemies", "true" ) );
1673  if ( can_warp_to || _Universe->AccessCockpit()->autoInProgress() )
1674  WarpToP( parent, target, false );
1677  if (logiccurtime <= 0) {
1680  if (isjumpable) {
1681  AfterburnerJumpTurnTowards( target );
1683  } else {
1684  last_jump_distance = FLT_MAX;
1685  ProcessLogic( *logic, false );
1686  }
1687  }
1688  } else if (queryAny( Order::MOVEMENT ) == NULL) {
1689  ExecuteNoEnemies();
1690  }
1691  }
1692  }
1693 #ifdef AGGDEBUG
1694  VSFileSystem::vs_fprintf( stderr, "endagg" );
1695  fflush( stderr );
1696 #endif
1697  if (getTimeCompression() > 3) {
1698  float mag = parent->GetVelocity().Magnitude();
1699  if (mag > .01)
1700  mag = 1/mag;
1702  parent->NetLocalForce = parent->NetForce = Vector( 0, 0, 0 );
1703  }
1704  target = parent->Target();
1705 
1706  isjumpable = target ? ( !target->GetDestinations().empty() ) : false;
1707  if (!isjumpable)
1708  if (parent->GetJumpStatus().drive >= 0)
1709  parent->ActivateJumpDrive( -1 );
1710 }
1711 
1713