From 8898ad9b25fca6afe2374d293a981db02a83d7e9 Mon Sep 17 00:00:00 2001 From: "FWoltermann@gmail.com" Date: Thu, 31 May 2012 14:46:27 +0000 Subject: Committing the documentation to svn to have it accessible online --- .../html/_fighter_tactical_a_i_8cpp_source.html | 679 +++++++++++++++++++++ 1 file changed, 679 insertions(+) create mode 100644 Doc/doxygen/html/_fighter_tactical_a_i_8cpp_source.html (limited to 'Doc/doxygen/html/_fighter_tactical_a_i_8cpp_source.html') diff --git a/Doc/doxygen/html/_fighter_tactical_a_i_8cpp_source.html b/Doc/doxygen/html/_fighter_tactical_a_i_8cpp_source.html new file mode 100644 index 0000000..b815689 --- /dev/null +++ b/Doc/doxygen/html/_fighter_tactical_a_i_8cpp_source.html @@ -0,0 +1,679 @@ + + + + + +Starshatter_Open: D:/SRC/StarshatterSVN/Stars45/FighterTacticalAI.cpp Source File + + + + + + + + + + + + + +
+
+ + + + + + +
+
Starshatter_Open +
+
Open source Starshatter engine
+
+
+ + + + + +
+
+ +
+
+
+ +
+ + + + +
+ +
+ +
+
+
FighterTacticalAI.cpp
+
+
+Go to the documentation of this file.
1 /* Project Starshatter 4.5
+
2  Destroyer Studios LLC
+
3  Copyright © 1997-2004. All Rights Reserved.
+
4 
+
5  SUBSYSTEM: Stars.exe
+
6  FILE: FighterTacticalAI.cpp
+
7  AUTHOR: John DiCamillo
+
8 
+
9 
+
10  OVERVIEW
+
11  ========
+
12  Fighter-specific mid-level (tactical) AI class
+
13 */
+
14 
+
15 #include "MemDebug.h"
+
16 #include "FighterTacticalAI.h"
+
17 #include "ShipAI.h"
+
18 #include "Ship.h"
+
19 #include "ShipDesign.h"
+
20 #include "Shot.h"
+
21 #include "Element.h"
+
22 #include "Instruction.h"
+
23 #include "RadioMessage.h"
+
24 #include "Sensor.h"
+
25 #include "Contact.h"
+
26 #include "WeaponGroup.h"
+
27 #include "Drive.h"
+
28 #include "Sim.h"
+
29 #include "StarSystem.h"
+
30 
+
31 #include "Game.h"
+
32 
+
33 // +----------------------------------------------------------------------+
+
34 
+
35 const int WINCHESTER_FIGHTER = 0;
+
36 const int WINCHESTER_ASSAULT = 1;
+
37 const int WINCHESTER_STRIKE = 2;
+
38 const int WINCHESTER_STATIC = 3;
+
39 
+
40 // +----------------------------------------------------------------------+
+
41 
+ +
43 : TacticalAI(ai), secondary_selection_time(0)
+
44 {
+
45  for (int i = 0; i < 4; i++)
+
46  winchester[i] = false;
+
47 
+
48  ai_level = ai->GetAILevel();
+
49 
+
50  switch (ai_level) {
+
51  default:
+
52  case 2: THREAT_REACTION_TIME = 1000; break;
+
53  case 1: THREAT_REACTION_TIME = 3000; break;
+
54  case 0: THREAT_REACTION_TIME = 6000; break;
+
55  }
+
56 }
+
57 
+
58 
+
59 // +--------------------------------------------------------------------+
+
60 
+ +
62 { }
+
63 
+
64 // +--------------------------------------------------------------------+
+
65 
+
66 bool
+ +
68 {
+ +
70 
+
71  int order = Instruction::PATROL;
+
72  roe = FLEXIBLE;
+
73 
+
74  if (navpt) {
+
75  order = navpt->Action();
+
76 
+
77  switch (order) {
+ +
79  case Instruction::DOCK:
+
80  case Instruction::RTB: roe = NONE;
+
81  break;
+
82 
+ + +
85  if (element_index > 1)
+
86  roe = DEFENSIVE;
+
87  break;
+
88 
+ + +
91  break;
+
92 
+ +
94  if (element_index > 1)
+
95  roe = DEFENSIVE;
+
96  else
+
97  roe = DIRECTED;
+
98  break;
+
99 
+
100  case Instruction::RECON:
+
101  case Instruction::STRIKE:
+ +
103  break;
+
104 
+
105  case Instruction::PATROL:
+ +
107  break;
+
108 
+
109  default: break;
+
110  }
+
111 
+
112  if (order == Instruction::STRIKE) {
+ +
114 
+
115  if (IsStrikeComplete(navpt)) {
+ +
117  }
+
118  }
+
119 
+
120  else if (order == Instruction::ASSAULT) {
+
121  if (ship->GetSensorMode() == Sensor::GM)
+ +
123 
+
124  if (IsStrikeComplete(navpt)) {
+ +
126  }
+
127  }
+
128 
+
129  else {
+
130  if (ship->GetSensorMode() == Sensor::GM)
+ +
132  }
+
133  }
+
134 
+
135  switch (roe) {
+
136  case NONE: ship->SetDirectorInfo(Game::GetText("ai.none")); break;
+
137  case SELF_DEFENSIVE: ship->SetDirectorInfo(Game::GetText("ai.self-defensive")); break;
+
138  case DEFENSIVE: ship->SetDirectorInfo(Game::GetText("ai.defensive")); break;
+
139  case DIRECTED: ship->SetDirectorInfo(Game::GetText("ai.directed")); break;
+
140  case FLEXIBLE: ship->SetDirectorInfo(Game::GetText("ai.flexible")); break;
+
141  default: ship->SetDirectorInfo(Game::GetText("ai.default")); break;
+
142  }
+
143 
+
144  return (navpt != 0);
+
145 }
+
146 
+
147 // +--------------------------------------------------------------------+
+
148 
+
149 void
+ +
151 {
+ +
153 
+
154  SimObject* target = ship_ai->GetTarget();
+
155 
+
156  if (target && (target->Type() == SimObject::SIM_SHIP) &&
+ +
158  SelectSecondaryForTarget((Ship*) target);
+ +
160  }
+
161 }
+
162 
+
163 // +--------------------------------------------------------------------+
+
164 
+
165 void
+ +
167 {
+
168  Ship* potential_target = tgt;
+
169 
+
170  if (!tgt) {
+
171  // try to target one of the element's objectives
+
172  // (if it shows up in the contact list)
+
173 
+
174  Element* elem = ship->GetElement();
+
175 
+
176  if (elem) {
+
177  Instruction* objective = elem->GetTargetObjective();
+
178 
+
179  if (objective) {
+
180  SimObject* obj_sim_obj = objective->GetTarget();
+
181  Ship* obj_tgt = 0;
+
182 
+
183  if (obj_sim_obj && obj_sim_obj->Type() == SimObject::SIM_SHIP)
+
184  obj_tgt = (Ship*) obj_sim_obj;
+
185 
+
186  if (obj_tgt && ship->FindContact(obj_tgt))
+
187  potential_target = obj_tgt;
+
188  }
+
189  }
+
190  }
+
191 
+
192  if (!CanTarget(potential_target))
+
193  potential_target = 0;
+
194 
+
195  ship_ai->SetTarget(potential_target);
+
196  SelectSecondaryForTarget(potential_target);
+
197 }
+
198 
+
199 // +--------------------------------------------------------------------+
+
200 
+
201 void
+ +
203 {
+
204  // NON-COMBATANTS do not pick targets of opportunity:
+
205  if (ship->GetIFF() == 0)
+
206  return;
+
207 
+
208  Ship* potential_target = 0;
+
209  Shot* current_shot_target = 0;
+
210 
+
211  // pick the closest combatant ship with a different IFF code:
+
212  double target_dist = 1.0e15;
+
213  double min_dist = 5.0e3;
+
214 
+
215  // FIGHTERS are primarily anti-air platforms, but may
+
216  // also attack smaller starships:
+
217 
+
218  Ship* ward = 0;
+
219  if (element_index > 1)
+
220  ward = ship->GetLeader();
+
221 
+
222  // commit range for patrol/sweep is 80 Km
+
223  // (about 2 minutes for a fighter at max speed)
+
224  if (roe == FLEXIBLE || roe == AGRESSIVE)
+
225  target_dist = ship->Design()->commit_range;
+
226 
+
227  if (roe < FLEXIBLE)
+
228  target_dist = 0.5 * ship->Design()->commit_range;
+
229 
+
230  int class_limit = Ship::LCA;
+
231 
+
232  if (ship->Class() == Ship::ATTACK)
+
233  class_limit = Ship::DESTROYER;
+
234 
+
235  ListIter<Contact> c_iter = ship->ContactList();
+
236  while (++c_iter) {
+
237  Contact* contact = c_iter.value();
+
238  Ship* c_ship = contact->GetShip();
+
239  Shot* c_shot = contact->GetShot();
+
240  int c_iff = contact->GetIFF(ship);
+
241  bool rogue = false;
+
242 
+
243  if (c_ship)
+
244  rogue = c_ship->IsRogue();
+
245 
+
246  if (!rogue && (c_iff <= 0 || c_iff == ship->GetIFF() || c_iff == 1000))
+
247  continue;
+
248 
+
249  // reasonable target?
+
250  if (c_ship && c_ship->Class() <= class_limit && !c_ship->InTransition()) {
+
251  if (!rogue) {
+
252  SimObject* ttgt = c_ship->GetTarget();
+
253 
+
254  // if we are self-defensive, is this contact engaging us?
+
255  if (roe == SELF_DEFENSIVE && ttgt != ship)
+
256  continue;
+
257 
+
258  // if we are defending, is this contact engaging us or our ward?
+
259  if (roe == DEFENSIVE && ttgt != ship && ttgt != ward)
+
260  continue;
+
261  }
+
262 
+
263  // found an enemy, check distance:
+
264  double dist = (ship->Location() - c_ship->Location()).length();
+
265 
+
266  if (dist < 0.75 * target_dist) {
+
267 
+
268  // if on patrol, check target distance from navpoint:
+
269  if (roe == FLEXIBLE && navpt) {
+
270  double ndist = (navpt->Location().OtherHand() - c_ship->Location()).length();
+
271  if (ndist > 80e3)
+
272  continue;
+
273  }
+
274 
+
275  potential_target = c_ship;
+
276  target_dist = dist;
+
277  }
+
278  }
+
279 
+
280  else if (c_shot && c_shot->IsDrone()) {
+
281  // found an enemy shot, do we have enough time to engage?
+
282  if (c_shot->GetEta() < 10)
+
283  continue;
+
284 
+
285  // found an enemy shot, check distance:
+
286  double dist = (ship->Location() - c_shot->Location()).length();
+
287 
+
288  if (!current_shot_target) {
+
289  current_shot_target = c_shot;
+
290  target_dist = dist;
+
291  }
+
292 
+
293  // is this shot a better target than the one we've found?
+
294  else {
+
295  Ship* ward = ship_ai->GetWard();
+
296 
+
297  if ((c_shot->IsTracking(ward) && !current_shot_target->IsTracking(ward)) ||
+
298  (dist < target_dist)) {
+
299  current_shot_target = c_shot;
+
300  target_dist = dist;
+
301  }
+
302  }
+
303  }
+
304  }
+
305 
+
306  if (current_shot_target) {
+
307  ship_ai->SetTarget(current_shot_target);
+
308  }
+
309  else {
+
310  ship_ai->SetTarget(potential_target);
+
311  SelectSecondaryForTarget(potential_target);
+
312  }
+
313 }
+
314 
+
315 // +--------------------------------------------------------------------+
+
316 
+
317 int
+ +
319 {
+
320  weps.clear();
+
321 
+
322  if (tgt) {
+ +
324  while (++iter) {
+
325  WeaponGroup* w = iter.value();
+
326 
+
327  if (w->Ammo() && w->CanTarget(tgt->Class()))
+
328  weps.append(w);
+
329  }
+
330  }
+
331 
+
332  return weps.size();
+
333 }
+
334 
+
335 void
+ +
337 {
+
338  if (tgt) {
+
339  int wix = WINCHESTER_FIGHTER;
+
340 
+
341  if (tgt->IsGroundUnit()) wix = WINCHESTER_STRIKE;
+
342  else if (tgt->IsStatic()) wix = WINCHESTER_STATIC;
+
343  else if (tgt->IsStarship()) wix = WINCHESTER_ASSAULT;
+
344 
+
345  WeaponGroup* best = 0;
+
346  List<WeaponGroup> weps;
+
347 
+
348  if (ListSecondariesForTarget(tgt, weps)) {
+
349  winchester[wix] = false;
+
350 
+
351  // select best weapon for the job:
+
352  double range = (ship->Location() - tgt->Location()).length();
+
353  double best_range = 0;
+
354  double best_damage = 0;
+
355 
+
356  ListIter<WeaponGroup> iter = weps;
+
357  while (++iter) {
+
358  WeaponGroup* w = iter.value();
+
359 
+
360  if (!best) {
+
361  best = w;
+
362 
+
363  WeaponDesign* d = best->GetDesign();
+
364  best_range = d->max_range;
+
365  best_damage = d->damage * d->ripple_count;
+
366 
+
367  if (best_range < range)
+
368  best = 0;
+
369  }
+
370 
+
371  else {
+
372  WeaponDesign* d = w->GetDesign();
+
373  double w_range = d->max_range;
+
374  double w_damage = d->damage * d->ripple_count;
+
375 
+
376  if (w_range > range) {
+
377  if (w_range < best_range || w_damage > best_damage)
+
378  best = w;
+
379  }
+
380  }
+
381  }
+
382 
+
383  // now cycle weapons until you pick the best one:
+
384  WeaponGroup* current_missile = ship->GetSecondaryGroup();
+
385 
+
386  if (current_missile && best && current_missile != best) {
+
387  ship->CycleSecondary();
+ +
389 
+
390  while (m != current_missile && m != best) {
+
391  ship->CycleSecondary();
+
392  m = ship->GetSecondaryGroup();
+
393  }
+
394  }
+
395  }
+
396 
+
397  else {
+
398  winchester[wix] = true;
+
399 
+
400  // if we have NO weapons that can hit this target,
+
401  // just drop it:
+
402 
+
403  Weapon* primary = ship->GetPrimary();
+
404  if (!primary || !primary->CanTarget(tgt->Class())) {
+
405  ship_ai->DropTarget(3);
+
406  ship->DropTarget();
+
407  }
+
408  }
+
409 
+
410  if (tgt->IsGroundUnit())
+ +
412 
+
413  else if (ship->GetSensorMode() == Sensor::GM)
+ +
415  }
+
416 }
+
417 
+
418 // +--------------------------------------------------------------------+
+
419 
+
420 void
+ +
422 {
+
423  // find the formation delta:
+
424  int s = element_index - 1;
+
425  Point delta(5*s, 0, -5*s);
+
426 
+
427  // diamond:
+
428  if (formation == Instruction::DIAMOND) {
+
429  switch (element_index) {
+
430  case 2: delta = Point( 12, -1, -10); break;
+
431  case 3: delta = Point(-12, -1, -10); break;
+
432  case 4: delta = Point( 0, -2, -20); break;
+
433  }
+
434  }
+
435 
+
436  // spread:
+
437  if (formation == Instruction::SPREAD) {
+
438  switch (element_index) {
+
439  case 2: delta = Point( 15, 0, 0); break;
+
440  case 3: delta = Point(-15, 0, 0); break;
+
441  case 4: delta = Point(-30, 0, 0); break;
+
442  }
+
443  }
+
444 
+
445  // box:
+
446  if (formation == Instruction::BOX) {
+
447  switch (element_index) {
+
448  case 2: delta = Point(15, 0, 0); break;
+
449  case 3: delta = Point( 0, -2, -20); break;
+
450  case 4: delta = Point(15, -2, -20); break;
+
451  }
+
452  }
+
453 
+
454  // trail:
+
455  if (formation == Instruction::TRAIL) {
+
456  delta = Point(0, s, -20*s);
+
457  }
+
458 
+
459  ship_ai->SetFormationDelta(delta * ship->Radius() * 2);
+
460 }
+
461 
+
462 // +--------------------------------------------------------------------+
+
463 
+
464 void
+ +
466 {
+
467  // pick the closest contact on Threat Warning System:
+
468  Ship* threat_ship = 0;
+
469  Shot* threat_missile = 0;
+
470  double threat_dist = 1e9;
+
471 
+
472  ListIter<Contact> c_iter = ship->ContactList();
+
473 
+
474  while (++c_iter) {
+
475  Contact* contact = c_iter.value();
+
476 
+
477  if (contact->Threat(ship) &&
+ +
479 
+
480  double rng = contact->Range(ship);
+
481 
+
482  if (contact->GetShot()) {
+
483  threat_missile = contact->GetShot();
+
484  }
+
485 
+
486  else if (rng < threat_dist && contact->GetShip()) {
+
487  Ship* candidate = contact->GetShip();
+
488 
+
489  if (candidate->InTransition())
+
490  continue;
+
491 
+
492  if (candidate->IsStarship() && rng < 50e3) {
+
493  threat_ship = candidate;
+
494  threat_dist = rng;
+
495  }
+
496 
+
497  else if (candidate->IsDropship() && rng < 25e3) {
+
498  threat_ship = candidate;
+
499  threat_dist = rng;
+
500  }
+
501 
+
502  // static and ground units:
+
503  else if (rng < 30e3) {
+
504  threat_ship = candidate;
+
505  threat_dist = rng;
+
506  }
+
507  }
+
508  }
+
509  }
+
510 
+
511  ship_ai->SetThreat(threat_ship);
+
512  ship_ai->SetThreatMissile(threat_missile);
+
513 }
+
514 
+
515 // +--------------------------------------------------------------------+
+
516 
+
517 bool
+ +
519 {
+
520  // wingmen can not call a halt to a strike:
+
521  if (!ship || element_index > 1)
+
522  return false;
+
523 
+
524  // if there's nothing to shoot at, we must be done:
+
525  if (!instr || !instr->GetTarget() || instr->GetTarget()->Life() == 0 ||
+
526  instr->GetTarget()->Type() != SimObject::SIM_SHIP)
+
527  return true;
+
528 
+
529  // break off strike only when ALL weapons are expended:
+
530  // (remember to check all relevant wingmen)
+
531  Element* element = ship->GetElement();
+
532  Ship* target = (Ship*) instr->GetTarget();
+
533 
+
534  if (!element)
+
535  return true;
+
536 
+
537  for (int i = 0; i < element->NumShips(); i++) {
+
538  Ship* s = element->GetShip(i+1);
+
539 
+
540  if (!s || s->Integrity() < 25) // || (s->Location() - target->Location()).length() > 250e3)
+
541  continue;
+
542 
+
543  ListIter<WeaponGroup> g_iter = s->Weapons();
+
544  while (++g_iter) {
+
545  WeaponGroup* w = g_iter.value();
+
546 
+
547  if (w->Ammo() && w->CanTarget(target->Class())) {
+
548  ListIter<Weapon> w_iter = w->GetWeapons();
+
549 
+
550  while (++w_iter) {
+
551  Weapon* weapon = w_iter.value();
+
552 
+
553  if (weapon->Status() > System::CRITICAL)
+
554  return false;
+
555  }
+
556  }
+
557  }
+
558  }
+
559 
+
560  // still here? we must be done!
+
561  return true;
+
562 }
+
+
+ + + + -- cgit v1.1