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 --- Doc/doxygen/html/_fighter_a_i_8cpp_source.html | 1948 ++++++++++++++++++++++++ 1 file changed, 1948 insertions(+) create mode 100644 Doc/doxygen/html/_fighter_a_i_8cpp_source.html (limited to 'Doc/doxygen/html/_fighter_a_i_8cpp_source.html') diff --git a/Doc/doxygen/html/_fighter_a_i_8cpp_source.html b/Doc/doxygen/html/_fighter_a_i_8cpp_source.html new file mode 100644 index 0000000..41cf3dd --- /dev/null +++ b/Doc/doxygen/html/_fighter_a_i_8cpp_source.html @@ -0,0 +1,1948 @@ + + + + + +Starshatter_Open: D:/SRC/StarshatterSVN/Stars45/FighterAI.cpp Source File + + + + + + + + + + + + + +
+
+ + + + + + +
+
Starshatter_Open +
+
Open source Starshatter engine
+
+
+ + + + + +
+
+ +
+
+
+ +
+ + + + +
+ +
+ +
+
+
FighterAI.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: FighterAI.cpp
+
7  AUTHOR: John DiCamillo
+
8 
+
9 
+
10  OVERVIEW
+
11  ========
+
12  Fighter (low-level) Artificial Intelligence class
+
13 */
+
14 
+
15 #include "MemDebug.h"
+
16 #include "FighterAI.h"
+
17 #include "FighterTacticalAI.h"
+
18 #include "Ship.h"
+
19 #include "Shot.h"
+
20 #include "Sensor.h"
+
21 #include "Element.h"
+
22 #include "ShipDesign.h"
+
23 #include "Instruction.h"
+
24 #include "Weapon.h"
+
25 #include "WeaponGroup.h"
+
26 #include "Drive.h"
+
27 #include "QuantumDrive.h"
+
28 #include "Farcaster.h"
+
29 #include "FlightComp.h"
+
30 #include "FlightDeck.h"
+
31 #include "Hangar.h"
+
32 #include "Sim.h"
+
33 #include "StarSystem.h"
+
34 #include "RadioMessage.h"
+
35 #include "RadioTraffic.h"
+
36 
+
37 #include "Game.h"
+
38 
+
39 static const double TIME_TO_DOCK = 30;
+
40 
+
41 // +----------------------------------------------------------------------+
+
42 
+ +
44 : ShipAI(s), brakes(0), drop_state(0), jink_time(0), evading(false),
+
45 decoy_missile(0), missile_time(0), terrain_warning(false), inbound(0),
+
46 rtb_code(0), form_up(false), over_threshold(false), time_to_dock(0),
+
47 go_manual(false)
+
48 {
+
49  ai_type = FIGHTER;
+
50  seek_gain = 22;
+
51  seek_damp = 0.55;
+
52  brakes = 0;
+
53  z_shift = 0;
+
54 
+
55  tactical = new(__FILE__,__LINE__) FighterTacticalAI(this);
+
56 }
+
57 
+
58 
+
59 // +--------------------------------------------------------------------+
+
60 
+ +
62 { }
+
63 
+
64 // +--------------------------------------------------------------------+
+
65 
+
66 static double frame_time = 0;
+
67 
+
68 void
+ +
70 {
+
71  if (!ship) return;
+
72 
+
73  evading = false;
+
74  inbound = ship->GetInbound();
+
75  missile_time -= s;
+
76 
+
77  int order = 0;
+
78 
+
79  if (navpt)
+
80  order = navpt->Action();
+
81 
+
82  if (inbound) {
+
83  form_up = false;
+
84  rtb_code = 1;
+
85 
+
86  // CHEAT LANDING:
+
87  if (inbound->Final() && time_to_dock > 0) {
+
88  FlightDeck* deck = inbound->GetDeck();
+
89  if (deck) {
+
90  Point dst = deck->EndPoint();
+
91  Point approach = deck->StartPoint() - dst;
+
92 
+
93  const Ship* carrier = deck->GetCarrier();
+
94 
+
95  Camera landing_cam;
+
96  landing_cam.Clone(carrier->Cam());
+
97  landing_cam.Yaw(deck->Azimuth());
+
98 
+
99  if (time_to_dock > TIME_TO_DOCK/2) {
+
100  double lr, lp, lw;
+
101  double sr, sp, sw;
+
102 
+
103  landing_cam.Orientation().ComputeEulerAngles(lr, lp, lw);
+
104  ship->Cam().Orientation().ComputeEulerAngles(sr, sp, sw);
+
105 
+
106  double nr = sr + s*(lr-sr);
+
107  double np = sp + s*(lp-sp);
+
108  double nw = sw + s*(lw-sw)*0.5;
+
109 
+
110  Camera work;
+
111  work.Aim(nr,np,nw);
+
112  landing_cam.Clone(work);
+
113  }
+
114 
+
115  ship->CloneCam(landing_cam);
+
116  ship->MoveTo(dst + approach * (time_to_dock / TIME_TO_DOCK));
+
117  ship->SetVelocity(carrier->Velocity() + ship->Heading() * 50);
+
118  ship->SetThrottle(50);
+
119  ship->ExecFLCSFrame();
+
120 
+
121  time_to_dock -= s;
+
122 
+
123  if (time_to_dock <= 0) {
+
124  deck->Dock(ship);
+
125  time_to_dock = 0;
+
126  }
+
127 
+
128  return;
+
129  }
+
130  }
+
131 
+
132  else if (ship->GetFlightPhase() == Ship::DOCKING) {
+
133  // deal with (pathological) moving carrier deck:
+
134 
+
135  FlightDeck* deck = inbound->GetDeck();
+
136  if (deck) {
+
137  Point dst = deck->EndPoint();
+
138 
+
139  if (ship->IsAirborne()) {
+
140  double alt = dst.y;
+
141  dst = ship->Location();
+
142  dst.y = alt;
+
143  }
+
144 
+
145  const Ship* carrier = deck->GetCarrier();
+
146 
+
147  Camera landing_cam;
+
148  landing_cam.Clone(carrier->Cam());
+
149  landing_cam.Yaw(deck->Azimuth());
+
150 
+
151  ship->CloneCam(landing_cam);
+
152  ship->MoveTo(dst);
+
153 
+
154  if (!ship->IsAirborne()) {
+
155  ship->SetVelocity(carrier->Velocity());
+
156  }
+
157  else {
+
158  Point taxi(landing_cam.vpn());
+
159  ship->SetVelocity(taxi * 95);
+
160  }
+
161 
+
162  ship->SetThrottle(0);
+
163  ship->ExecFLCSFrame();
+
164  }
+
165 
+
166  return;
+
167  }
+
168  }
+
169  else {
+
170  Instruction* orders = ship->GetRadioOrders();
+
171 
+
172  if (orders &&
+
173  (orders->Action() == RadioMessage::WEP_HOLD ||
+
174  orders->Action() == RadioMessage::FORM_UP)) {
+
175  form_up = true;
+
176  rtb_code = 0;
+
177  }
+
178  else {
+
179  form_up = false;
+
180  }
+
181  }
+
182 
+
183  if (!target && order != Instruction::STRIKE)
+ +
185 
+
186  ShipAI::ExecFrame(s); // this must be the last line of this method
+
187 
+
188  // IT IS NOT SAFE TO PLACE CODE HERE
+
189  // if this class decides to break orbit,
+
190  // this object will be deleted during
+
191  // ShipAI::ExecFrame() (which calls
+
192  // FighterAI::Navigator() - see below)
+
193 }
+
194 
+
195 // +--------------------------------------------------------------------+
+
196 
+
197 void
+ +
199 {
+
200  distance = 0;
+
201 
+
202  // ALWAYS complete initial launch navpt:
+
203  if (!navpt) {
+ + +
206  navpt = 0;
+
207  }
+
208 
+
209  if (navpt && navpt->Action() == Instruction::LAUNCH) {
+
210  if (navpt->Status() != Instruction::COMPLETE) {
+ +
212 
+
213  // transform into camera coords:
+ +
215  ship->SetDirectorInfo(Game::GetText("ai.launch"));
+
216  return;
+
217  }
+
218  else {
+
219  navpt = 0;
+
220  }
+
221  }
+
222 
+
223  // runway takeoff:
+
224  else if (takeoff) {
+
225  obj_w = ship->Location() + ship->Heading() * 10e3;
+
226  obj_w.y = ship->Location().y + 2e3;
+
227 
+
228  // transform into camera coords:
+ +
230  ship->SetDirectorInfo(Game::GetText("ai.takeoff"));
+
231  return;
+
232  }
+
233 
+
234  // approaching a carrier or runway:
+
235  else if (inbound) {
+
236  FlightDeck* deck = inbound->GetDeck();
+
237 
+
238  if (!deck) {
+
239  objective = Point();
+
240  return;
+
241  }
+
242 
+
243  // initial approach
+
244  if (inbound->Approach() > 0 || !inbound->Cleared()) {
+ +
246 
+
247  distance = (obj_w - ship->Location()).length();
+
248 
+
249  // transform into camera coords:
+ +
251  ship->SetDirectorInfo(Game::GetText("ai.inbound"));
+
252 
+
253  return;
+
254  }
+
255 
+
256  // final approach
+
257  else {
+
258  ship->SetDirectorInfo(Game::GetText("ai.finals"));
+
259 
+
260  obj_w = deck->StartPoint();
+
261  if (inbound->Final()) {
+
262  obj_w = deck->EndPoint();
+
263 
+
264  if (deck->OverThreshold(ship)) {
+
265  obj_w = deck->MountLocation();
+
266  over_threshold = true;
+
267  }
+
268  }
+
269 
+
270  distance = (obj_w - ship->Location()).length();
+
271 
+
272  // transform into camera coords:
+ +
274 
+
275  return;
+
276  }
+
277  }
+
278 
+
279  // not inbound yet, check for RTB order:
+
280  else {
+
281  Instruction* orders = (Instruction*) ship->GetRadioOrders();
+
282  int action = 0;
+
283 
+
284  if (orders)
+
285  action = orders->Action();
+
286 
+
287  if (navpt && !action) {
+ +
289  if (distance < 5e3) {
+
290  action = navpt->Action();
+
291  }
+
292  }
+
293 
+
294  if (action == RadioMessage::RTB ||
+
295  action == RadioMessage::DOCK_WITH) {
+
296 
+
297  Ship* controller = ship->GetController();
+
298 
+
299  if (orders && orders->Action() == RadioMessage::DOCK_WITH && orders->GetTarget()) {
+
300  controller = (Ship*) orders->GetTarget();
+
301  }
+
302 
+
303  else if (navpt && navpt->Action() == RadioMessage::DOCK_WITH && navpt->GetTarget()) {
+
304  controller = (Ship*) navpt->GetTarget();
+
305  }
+
306 
+
307  ReturnToBase(controller);
+
308 
+
309  if (rtb_code)
+
310  return;
+
311  }
+
312  }
+
313 
+ +
315 }
+
316 
+
317 void
+ +
319 {
+
320  rtb_code = 0;
+
321 
+
322  if (controller) {
+
323  SimRegion* self_rgn = ship->GetRegion();
+
324  SimRegion* rtb_rgn = controller->GetRegion();
+
325 
+
326  if (self_rgn && !rtb_rgn) {
+
327  rtb_rgn = self_rgn;
+
328  }
+
329 
+
330  if (self_rgn && rtb_rgn && self_rgn != rtb_rgn) {
+
331  // is the carrier in orbit above us
+
332  // (or on the ground below us)?
+
333 
+
334  if (rtb_rgn->GetOrbitalRegion()->Primary() ==
+
335  self_rgn->GetOrbitalRegion()->Primary()) {
+
336 
+
337  Point npt = rtb_rgn->Location() - self_rgn->Location();
+
338  obj_w = npt.OtherHand();
+
339 
+
340  // distance from self to navpt:
+
341  distance = Point(obj_w - ship->Location()).length();
+
342 
+
343  // transform into camera coords:
+ +
345 
+
346  if (rtb_rgn->IsAirSpace()) {
+
347  drop_state = -1;
+
348  }
+
349  else if (rtb_rgn->IsOrbital()) {
+
350  drop_state = 1;
+
351  }
+
352 
+
353  rtb_code = 2;
+
354  }
+
355 
+
356  // try to find a jumpgate that will take us home:
+
357  else {
+
358  QuantumDrive* qdrive = ship->GetQuantumDrive();
+
359  bool use_farcaster = !qdrive ||
+
360  !qdrive->IsPowerOn() ||
+
361  qdrive->Status() < System::DEGRADED;
+
362 
+
363  if (use_farcaster) {
+
364  if (!farcaster) {
+
365  ListIter<Ship> s = self_rgn->Ships();
+
366  while (++s && !farcaster) {
+
367  if (s->GetFarcaster()) {
+
368  const Ship* dest = s->GetFarcaster()->GetDest();
+
369  if (dest && dest->GetRegion() == rtb_rgn) {
+
370  farcaster = s->GetFarcaster();
+
371  }
+
372  }
+
373  }
+
374  }
+
375 
+
376  if (farcaster) {
+
377  Point apt = farcaster->ApproachPoint(0);
+
378  Point npt = farcaster->StartPoint();
+
379  double r1 = (ship->Location() - npt).length();
+
380 
+
381  if (r1 > 50e3) {
+
382  obj_w = apt;
+
383  distance = r1;
+ +
385  }
+
386 
+
387  else {
+
388  double r2 = (ship->Location() - apt).length();
+
389  double r3 = (npt - apt).length();
+
390 
+
391  if (r1+r2 < 1.2*r3) {
+
392  obj_w = npt;
+
393  distance = r1;
+ +
395  }
+
396  else {
+
397  obj_w = apt;
+
398  distance = r2;
+ +
400  }
+
401  }
+
402 
+
403  rtb_code = 3;
+
404  }
+
405 
+
406  // can't find a way back home, ignore the RTB order:
+
407  else {
+ +
409  rtb_code = 0;
+
410  return;
+
411  }
+
412  }
+
413  else if (qdrive) {
+
414  if (qdrive->ActiveState() == QuantumDrive::ACTIVE_READY) {
+
415  qdrive->SetDestination(rtb_rgn, controller->Location());
+
416  qdrive->Engage();
+
417  }
+
418 
+
419  rtb_code = 3;
+
420  }
+
421  }
+
422  }
+
423 
+
424  else {
+
425  obj_w = controller->Location();
+
426 
+
427  distance = (obj_w - ship->Location()).length();
+
428 
+
429  // transform into camera coords:
+ +
431  ship->SetDirectorInfo(Game::GetText("ai.return-to-base"));
+
432 
+
433  rtb_code = 1;
+
434  }
+
435  }
+
436 }
+
437 
+
438 // +--------------------------------------------------------------------+
+
439 
+
440 void
+ +
442 {
+
443  SimRegion* self_rgn = ship->GetRegion();
+
444  SimRegion* nav_rgn = navpt->Region();
+
445 
+
446  if (self_rgn && !nav_rgn) {
+
447  nav_rgn = self_rgn;
+
448  navpt->SetRegion(nav_rgn);
+
449  }
+
450 
+
451  if (self_rgn && nav_rgn && self_rgn != nav_rgn) {
+
452  if (nav_rgn->GetOrbitalRegion()->Primary() ==
+
453  self_rgn->GetOrbitalRegion()->Primary()) {
+
454 
+
455  Point npt = nav_rgn->Location() - self_rgn->Location();
+
456  obj_w = npt.OtherHand();
+
457 
+
458  // distance from self to navpt:
+
459  distance = Point(obj_w - ship->Location()).length();
+
460 
+
461  // transform into camera coords:
+ +
463 
+
464  if (nav_rgn->IsAirSpace()) {
+
465  drop_state = -1;
+
466  }
+
467  else if (nav_rgn->IsOrbital()) {
+
468  drop_state = 1;
+
469  }
+
470 
+
471  return;
+
472  }
+
473 
+
474  else {
+ +
476 
+
477  if (q) {
+ + +
480  q->Engage();
+
481  return;
+
482  }
+
483  }
+
484  }
+
485  }
+
486 
+ +
488 }
+
489 
+
490 // +--------------------------------------------------------------------+
+
491 
+
492 Point
+ +
494 {
+
495  if (ship) {
+
496  WeaponDesign* wep_design = ship->GetPrimaryDesign();
+
497 
+
498  if (target && wep_design) {
+
499  Point aim_vec = ship->Heading();
+
500  aim_vec.Normalize();
+
501 
+
502  Point shot_vel = ship->Velocity() + aim_vec * wep_design->speed;
+
503  return shot_vel - target->Velocity();
+
504  }
+
505 
+
506  else if (target) {
+
507  return ship->Velocity() - target->Velocity();
+
508  }
+
509 
+
510  else {
+
511  return ship->Velocity();
+
512  }
+
513  }
+
514 
+
515  return Point(1,0,0);
+
516 }
+
517 
+
518 // +--------------------------------------------------------------------+
+
519 
+
520 void
+ +
522 {
+
523  go_manual = false;
+
524 
+
525  if (takeoff) {
+
526  accumulator.Clear();
+
527  magnitude = 0;
+
528  brakes = 0;
+
529  z_shift = 0;
+
530 
+ +
532  HelmControl();
+
533  ThrottleControl();
+
534  ship->ExecFLCSFrame();
+
535  return;
+
536  }
+
537 
+
538  Element* elem = ship->GetElement();
+
539 
+
540  if (elem) {
+
541  Ship* lead = elem->GetShip(1);
+
542 
+
543  if (lead && lead != ship) {
+
544  if (lead->IsDropping() && !ship->IsDropping()) {
+
545  ship->DropOrbit();
+
546  // careful: this object has just been deleted!
+
547  return;
+
548  }
+
549 
+
550  if (lead->IsAttaining() && !ship->IsAttaining()) {
+
551  ship->MakeOrbit();
+
552  // careful: this object has just been deleted!
+
553  return;
+
554  }
+
555  }
+
556 
+
557  else {
+
558  if (drop_state < 0) {
+
559  ship->DropOrbit();
+
560  // careful: this object has just been deleted!
+
561  return;
+
562  }
+
563 
+
564  if (drop_state > 0) {
+
565  ship->MakeOrbit();
+
566  // careful: this object has just been deleted!
+
567  return;
+
568  }
+
569  }
+
570  }
+
571 
+
572  int order = 0;
+
573 
+
574  if (navpt)
+
575  order = navpt->Action();
+
576 
+
577  if (rtb_code == 1 && navpt && navpt->Status() < Instruction::SKIPPED &&
+
578  !inbound && distance < 35e3) { // (this should be distance to the ship)
+
579 
+
580  if (order == Instruction::RTB) {
+
581  Ship* controller = ship->GetController();
+
582  Hangar* hangar = controller ? controller->GetHangar() : 0;
+
583 
+
584  if (hangar && hangar->CanStow(ship)) {
+
585  for (int i = 0; i < elem->NumShips(); i++) {
+
586  Ship* s = elem->GetShip(i+1);
+
587 
+
588  if (s && s->GetDirector() && s->GetDirector()->Type() >= ShipAI::FIGHTER)
+ +
590  }
+
591 
+
592  if (element_index == 1)
+ +
594  }
+
595 
+
596  else {
+
597  if (element_index == 1) {
+
598  ::Print("WARNING: FighterAI NAVPT RTB, but no controller or hangar found for ship '%s'\n", ship->Name());
+ +
600  }
+
601  }
+
602  }
+
603 
+
604  else {
+
605  Ship* dock_target = (Ship*) navpt->GetTarget();
+
606  if (dock_target) {
+
607  for (int i = 0; i < elem->NumShips(); i++) {
+
608  Ship* s = elem->GetShip(i+1);
+
609 
+
610  if (s) {
+
611  RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(dock_target, s, RadioMessage::CALL_INBOUND);
+ +
613  }
+
614  }
+
615 
+
616  if (element_index == 1)
+ +
618  }
+
619 
+
620  else {
+
621  if (element_index == 1) {
+
622  ::Print("WARNING: FighterAI NAVPT DOCK, but no dock target found for ship '%s'\n", ship->Name());
+ +
624  }
+
625  }
+
626  }
+
627  }
+
628 
+
629  if (target)
+
630  ship->SetDirectorInfo(Game::GetText("ai.seek-target"));
+
631 
+
632  accumulator.Clear();
+
633  magnitude = 0;
+
634  brakes = 0;
+
635  z_shift = 0;
+
636 
+
637  hold = false;
+
638  if ((ship->GetElement() && ship->GetElement()->GetHoldTime() > 0) ||
+
639  (navpt && navpt->Status() == Instruction::COMPLETE && navpt->HoldTime() > 0))
+
640  hold = true;
+
641 
+
642  if (ship->MissionClock() < 10000) {
+
643  if (ship->IsAirborne())
+ +
645  }
+
646 
+
647  else if ((farcaster && distance < 20e3) || (inbound && inbound->Final())) {
+ +
649  }
+
650 
+
651  else {
+
652  if (!ship->IsAirborne() || ship->AltitudeAGL() > 100)
+
653  ship->RaiseGear();
+
654 
+ +
656  Steer avoid = AvoidCollision();
+
657 
+
658  if (other && inbound && inbound->GetDeck() && inbound->Cleared()) {
+
659  if (other != (SimObject*) inbound->GetDeck()->GetCarrier())
+
660  Accumulate(avoid);
+
661  }
+
662  else {
+
663  Accumulate(avoid);
+
664  }
+
665 
+
666  if (!too_close && !hold && !terrain_warning) {
+ + +
669  }
+
670  }
+
671 
+
672  HelmControl();
+
673  ThrottleControl();
+
674  FireControl();
+
675  AdjustDefenses();
+
676 
+
677  ship->ExecFLCSFrame();
+
678 }
+
679 
+
680 // +--------------------------------------------------------------------+
+
681 
+
682 void
+ +
684 {
+
685  Camera* cam = ((Camera*) &(ship->Cam()));
+
686  Point vrt = cam->vrt();
+
687  double deflection = vrt.y;
+
688  double theta = 0;
+
689  bool formation = element_index > 1;
+
690  bool station_keeping = distance < 0;
+
691  bool inverted = cam->vup().y < -0.5;
+
692  Ship* ward = ship->GetWard();
+
693 
+
694  if (takeoff || inbound || station_keeping)
+
695  formation = false;
+
696 
+
697  if (takeoff || navpt || farcaster || patrol || inbound || rtb_code || target || ward || threat || formation) {
+
698  // are we being asked to flee?
+
699  if (fabs(accumulator.yaw) == 1.0 && accumulator.pitch == 0.0) {
+
700  accumulator.pitch = -0.7f;
+
701  accumulator.yaw *= 0.25f;
+
702 
+
703  if (ship->IsAirborne() && ship->GetFlightModel() == 0)
+
704  accumulator.pitch = -0.45f;
+
705 
+
706  // low ai -> lower turning rate
+
707  accumulator.pitch += 0.1f * (2-ai_level);
+
708  }
+
709 
+
710  ship->ApplyRoll((float) (accumulator.yaw * -0.7));
+
711  ship->ApplyYaw((float) (accumulator.yaw * 0.2));
+
712 
+
713  if (fabs(accumulator.yaw) > 0.5 && fabs(accumulator.pitch) < 0.1)
+
714  accumulator.pitch -= 0.1f;
+
715 
+
716  ship->ApplyPitch((float) accumulator.pitch);
+
717  }
+
718 
+
719  else {
+
720  ship->SetDirectorInfo(Game::GetText("ai.station-keeping"));
+
721  station_keeping = true;
+
722 
+
723  // go into a slow orbit if airborne:
+
724  if (ship->IsAirborne() && ship->Class() < Ship::LCA) {
+
725  accumulator.brake = 0.2;
+
726  accumulator.stop = 0;
+
727 
+
728  double compass_pitch = ship->CompassPitch();
+
729  double desired_bank = -PI/4;
+
730  double current_bank = asin(deflection);
+
731  double theta = desired_bank - current_bank;
+
732  ship->ApplyRoll(theta);
+
733 
+
734  double coord_pitch = compass_pitch - 0.2 * fabs(current_bank);
+
735  ship->ApplyPitch(coord_pitch);
+
736  }
+
737  else {
+
738  accumulator.brake = 1;
+
739  accumulator.stop = 1;
+
740  }
+
741  }
+
742 
+
743  // if not turning, roll to orient with world coords:
+
744  if (ship->Design()->auto_roll > 0) {
+
745  if (fabs(accumulator.pitch) < 0.1 && fabs(accumulator.yaw) < 0.25) {
+
746  // zolon spiral behavior:
+
747  if (ship->Design()->auto_roll > 1) {
+
748  if ((element_index + (ship->MissionClock()>>10)) & 0x4)
+
749  ship->ApplyRoll( 0.60);
+
750  else
+
751  ship->ApplyRoll(-0.35);
+
752  }
+
753 
+
754  // normal behavior - roll to upright:
+
755  else if (fabs(deflection) > 0.1 || inverted) {
+
756  double theta = asin(deflection/vrt.length()) * 0.5;
+
757  ship->ApplyRoll(-theta);
+
758  }
+
759  }
+
760  }
+
761 
+
762  // if not otherwise occupied, pitch to orient with world coords:
+
763  if (station_keeping && (!ship->IsAirborne() || ship->Class() < Ship::LCA)) {
+
764  Point heading = ship->Heading();
+
765  double pitch_deflection = heading.y;
+
766 
+
767  if (fabs(pitch_deflection) > 0.05) {
+
768  double rho = asin(pitch_deflection) * 3;
+
769  ship->ApplyPitch(rho);
+
770  }
+
771  }
+
772 
+
773  ship->SetTransX(0);
+
774  ship->SetTransY(0);
+ + +
777 }
+
778 
+
779 void
+ +
781 {
+
782  Element* elem = ship->GetElement();
+
783  double ship_speed = ship->Velocity() * ship->Heading();
+
784  double desired = 1000;
+
785  bool formation = element_index > 1;
+
786  bool station_keeping = distance < 0;
+
787  bool augmenter = false;
+
788  Ship* ward = ship->GetWard();
+
789 
+
790  if (inbound || station_keeping)
+
791  formation = false;
+
792 
+
793  // LAUNCH / TAKEOFF
+
794  if (ship->MissionClock() < 10000) {
+
795  formation = false;
+
796  throttle = 100;
+
797  brakes = 0;
+
798  }
+
799 
+
800  // STATION KEEPING
+
801  else if (station_keeping) {
+
802  // go into a slow orbit if airborne:
+
803  if (ship->IsAirborne() && ship->Class() < Ship::LCA) {
+
804  throttle = 30;
+
805  brakes = 0;
+
806  }
+
807  else {
+
808  throttle = 0;
+
809  brakes = 1;
+
810  }
+
811  }
+
812 
+
813  // TRY TO STAY AIRBORNE, YES?
+
814  else if (ship->IsAirborne() && ship_speed < 250 && ship->Class() < Ship::LCA) {
+
815  throttle = 100;
+
816  brakes = 0;
+
817 
+
818  if (ship_speed < 200)
+
819  augmenter = true;
+
820  }
+
821 
+
822  // INBOUND
+
823  else if (inbound) {
+
824  double carrier_speed = inbound->GetDeck()->GetCarrier()->Velocity().length();
+
825  desired = 250 + carrier_speed;
+
826 
+
827  if (distance > 25.0e3)
+
828  desired = 750 + carrier_speed;
+
829 
+
830  else if (ship->IsAirborne())
+
831  desired = 300;
+
832 
+
833  else if (inbound->Final())
+
834  desired = 75 + carrier_speed;
+
835 
+
836  throttle = 0;
+
837 
+
838  // holding short?
+
839  if (inbound->Approach() == 0 && !inbound->Cleared() &&
+
840  distance < 2000 && !ship->IsAirborne())
+
841  desired = 0;
+
842 
+
843  if (ship_speed > desired+5)
+
844  brakes = 0.25;
+
845 
+
846  else if (ship->IsAirborne() || Ship::GetFlightModel() > 0) {
+
847  throttle = old_throttle + 1;
+
848  }
+
849 
+
850  else if (ship_speed < 0.85 * desired) {
+
851  throttle = 100;
+
852 
+
853  if (ship_speed < 0 && ship->GetFuelLevel() > 10)
+
854  augmenter = true;
+
855  }
+
856 
+
857  else if (ship_speed < desired-5) {
+
858  throttle = 30;
+
859  }
+
860  }
+
861 
+
862  else if (rtb_code || farcaster) {
+
863  desired = 750;
+
864 
+
865  if (threat || threat_missile) {
+
866  throttle = 100;
+
867 
+
868  if (!threat_missile && ship->GetFuelLevel() > 15)
+
869  augmenter = true;
+
870  }
+
871 
+
872  else {
+
873  throttle = 0;
+
874 
+
875  if (ship_speed > desired+5)
+
876  brakes = 0.25;
+
877 
+
878  else if (Ship::GetFlightModel() > 0) {
+
879  throttle = old_throttle + 1;
+
880  }
+
881 
+
882  else if (ship_speed < 0.85 * desired) {
+
883  throttle = 100;
+
884 
+
885  if (ship_speed < 0 && ship->GetFuelLevel() > 10)
+
886  augmenter = true;
+
887  }
+
888 
+
889  else if (ship_speed < desired-5) {
+
890  throttle = 30;
+
891  }
+
892  }
+
893  }
+
894 
+
895  // RUN AWAY!!!
+
896  else if (evading) {
+
897  throttle = 100;
+
898 
+
899  if (!threat_missile && ship->GetFuelLevel() > 15)
+
900  augmenter = true;
+
901  }
+
902 
+
903  // PATROL AND FORMATION
+
904  else if (!navpt && !target && !ward) {
+
905  if (!elem || !formation) { // element lead
+
906  if (patrol) {
+
907  desired = 250;
+
908 
+
909  if (distance > 10e3)
+
910  desired = 750;
+
911 
+
912  if (ship_speed > desired+5) {
+
913  brakes = 0.25;
+
914  throttle = old_throttle - 5;
+
915  }
+
916 
+
917  else if (ship_speed < 0.85 * desired) {
+
918  throttle = 100;
+
919 
+
920  if (ship_speed < 0 && ship->GetFuelLevel() > 10)
+
921  augmenter = true;
+
922  }
+
923 
+
924  else if (ship_speed < desired-5)
+
925  throttle = old_throttle + 5;
+
926  }
+
927 
+
928  else {
+
929  throttle = 35;
+
930 
+
931  if (threat)
+
932  throttle = 100;
+
933 
+ +
935 
+
936  if (brakes > 0.1)
+
937  throttle = 0;
+
938  }
+
939  }
+
940 
+
941  else { // wingman
+
942  Ship* lead = elem->GetShip(1);
+
943  double zone = ship->Radius() * 3;
+
944 
+
945  if (lead)
+
946  desired = lead->Velocity() * lead->Heading();
+
947 
+
948  if (fabs(slot_dist) < distance/4) // try to prevent porpoising
+ +
950 
+
951  else if (slot_dist > zone*2) {
+
952  throttle = 100;
+
953 
+
954  if (objective.z > 10e3 && ship_speed < desired && ship->GetFuelLevel() > 25)
+
955  augmenter = true;
+
956  }
+
957 
+
958  else if (slot_dist > zone)
+
959  throttle = lead->Throttle() + 10;
+
960 
+
961  else if (slot_dist < -zone*2) {
+
962  throttle = old_throttle - 10;
+
963  brakes = 1;
+
964  }
+
965 
+
966  else if (slot_dist < -zone) {
+ +
968  brakes = 0.5;
+
969  }
+
970 
+
971  else if (lead) {
+
972  double lv = lead->Velocity().length();
+
973  double sv = ship_speed;
+
974  double dv = lv-sv;
+
975  double dt = 0;
+
976 
+
977  if (dv > 0) dt = dv * 1e-5 * frame_time;
+
978  else if (dv < 0) dt = dv * 1e-2 * frame_time;
+
979 
+
980  throttle = old_throttle + dt;
+
981  }
+
982 
+
983  else {
+ +
985  }
+
986  }
+
987  }
+
988 
+
989  // TARGET/WARD/NAVPOINT SEEKING
+
990  else {
+ +
992 
+
993  if (target) {
+
994  desired = 1250;
+
995 
+
996  if (ai_level < 1) {
+
997  throttle = 70;
+
998  }
+
999 
+
1000  else if (ship->IsAirborne()) {
+
1001  throttle = 100;
+
1002 
+
1003  if (!threat_missile && fabs(objective.z) > 6e3 && ship->GetFuelLevel() > 25)
+
1004  augmenter = true;
+
1005  }
+
1006 
+
1007  else {
+
1008  throttle = 100;
+
1009 
+
1010  if (objective.z > 20e3 && ship_speed < desired && ship->GetFuelLevel() > 35)
+
1011  augmenter = true;
+
1012 
+
1013  else if (objective.z > 0 && objective.z < 10e3)
+
1014  throttle = 50;
+
1015  }
+
1016  }
+
1017 
+
1018  else if (ward) {
+
1019  double d = (ship->Location() - ward->Location()).length();
+
1020 
+
1021  if (d > 5000) {
+
1022  if (ai_level < 1)
+
1023  throttle = 50;
+
1024  else
+
1025  throttle = 80;
+
1026  }
+
1027  else {
+
1028  double speed = ward->Velocity().length();
+
1029 
+
1030  if (speed > 0) {
+
1031  if (ship_speed > speed) {
+
1032  throttle = old_throttle - 5;
+
1033  brakes = 0.25;
+
1034  }
+
1035  else if (ship_speed < speed - 10) {
+
1036  throttle = old_throttle + 1;
+
1037  }
+
1038  }
+
1039  }
+
1040  }
+
1041 
+
1042  else if (navpt) {
+
1043  desired = navpt->Speed();
+
1044 
+
1045  if (hold) {
+
1046  // go into a slow orbit if airborne:
+
1047  if (ship->IsAirborne() && ship->Class() < Ship::LCA) {
+
1048  throttle = 25;
+
1049  brakes = 0;
+
1050  }
+
1051  else {
+
1052  throttle = 0;
+
1053  brakes = 1;
+
1054  }
+
1055  }
+
1056 
+
1057  else if (desired > 0) {
+
1058  if (ship_speed > desired) {
+
1059  throttle = old_throttle - 5;
+
1060  brakes = 0.25;
+
1061  }
+
1062 
+
1063  else if (ship_speed < 0.85 * desired) {
+
1064  throttle = 100;
+
1065 
+
1066  if ((ship->IsAirborne() || ship_speed < 0.35 * desired) && ship->GetFuelLevel() > 30)
+
1067  augmenter = true;
+
1068  }
+
1069 
+
1070  else if (ship_speed < desired - 10) {
+
1071  throttle = old_throttle + 1;
+
1072  }
+
1073 
+
1074  else if (Ship::GetFlightModel() > 0) {
+ +
1076  }
+
1077  }
+
1078  }
+
1079 
+
1080  else {
+
1081  throttle = 0;
+
1082  brakes = 1;
+
1083  }
+
1084  }
+
1085 
+
1086  if (ship->IsAirborne() && throttle < 20 && ship->Class() < Ship::LCA)
+
1087  throttle = 20;
+
1088  else if (ship->Design()->auto_roll > 1 && throttle < 5)
+
1089  throttle = 5;
+
1090  else if (throttle < 0)
+
1091  throttle = 0;
+
1092 
+ +
1094  ship->SetThrottle((int) throttle);
+
1095  ship->SetAugmenter(augmenter);
+
1096 
+
1097  if (accumulator.stop && ship->GetFLCS() != 0)
+
1098  ship->GetFLCS()->FullStop();
+
1099 
+
1100  else if (ship_speed > 1 && brakes > 0)
+ +
1102 
+
1103  else if (throttle > 10 && (ship->GetEMCON() < 2 || ship->GetFuelLevel() < 10))
+ +
1105 }
+
1106 
+
1107 // +--------------------------------------------------------------------+
+
1108 
+
1109 Steer
+ +
1111 {
+
1112  Steer avoid;
+
1113 
+
1114  terrain_warning = false;
+
1115 
+
1116  if (!ship || !ship->GetRegion() || !ship->GetRegion()->IsActive() ||
+ +
1118  return avoid;
+
1119 
+
1120  if (ship->IsAirborne() && ship->GetFlightPhase() == Ship::ACTIVE) {
+
1121  // too high?
+
1122  if (ship->AltitudeMSL() > 25e3) {
+
1123  if (!navpt || (navpt->Region() == ship->GetRegion() && navpt->Location().z < 27e3)) {
+
1124  terrain_warning = true;
+
1125  ship->SetDirectorInfo(Game::GetText("ai.too-high"));
+
1126 
+
1127  // where will we be?
+
1128  Point selfpt = ship->Location() + ship->Velocity() + Point(0, -15e3, 0);
+
1129 
+
1130  // transform into camera coords:
+
1131  Point obj = Transform(selfpt);
+
1132 
+
1133  // head down!
+
1134  avoid = Seek(obj);
+
1135  }
+
1136  }
+
1137 
+
1138  // too low?
+
1139  else if (ship->AltitudeAGL() < 2500) {
+
1140  terrain_warning = true;
+
1141  ship->SetDirectorInfo(Game::GetText("ai.too-low"));
+
1142 
+
1143  // way too low?
+
1144  if (ship->AltitudeAGL() < 1500) {
+
1145  ship->SetDirectorInfo(Game::GetText("ai.way-too-low"));
+
1146  target = 0;
+
1147  drop_time = 5;
+
1148  }
+
1149 
+
1150  // where will we be?
+
1151  Point selfpt = ship->Location() + ship->Velocity() + Point(0, 10e3, 0);
+
1152 
+
1153  // transform into camera coords:
+
1154  Point obj = Transform(selfpt);
+
1155 
+
1156  // pull up!
+
1157  avoid = Seek(obj);
+
1158  }
+
1159  }
+
1160 
+
1161  return avoid;
+
1162 }
+
1163 
+
1164 // +--------------------------------------------------------------------+
+
1165 
+
1166 Steer
+ +
1168 {
+
1169  if (ship->GetFlightPhase() < Ship::ACTIVE)
+
1170  return Seek(objective);
+
1171 
+
1172  Ship* ward = ship->GetWard();
+
1173 
+
1174  if ((!target && !ward && !navpt && !farcaster && !patrol && !inbound && !rtb_code) || ship->MissionClock() < 10000) {
+
1175  if (element_index > 1) {
+
1176  // break formation if threatened:
+
1177  if (threat_missile)
+
1178  return Steer();
+
1179 
+
1180  else if (threat && !form_up)
+
1181  return Steer();
+
1182 
+
1183  // otherwise, keep in formation:
+
1184  return SeekFormationSlot();
+
1185  }
+
1186  else {
+
1187  return Steer();
+
1188  }
+
1189  }
+
1190 
+
1191  if (patrol) {
+
1192  Steer result = Seek(objective);
+
1193  ship->SetDirectorInfo(Game::GetText("ai.seek-patrol-point"));
+
1194 
+
1195  if (distance < 10 * self->Radius()) {
+
1196  patrol = 0;
+
1197  result.brake = 1;
+
1198  result.stop = 1;
+
1199  }
+
1200 
+
1201  return result;
+
1202  }
+
1203 
+
1204  if (inbound) {
+
1205  Steer result = Seek(objective);
+
1206 
+
1207  if (over_threshold && objective.z < 0) {
+
1208  result = Steer();
+
1209  result.brake = 1;
+
1210  result.stop = 1;
+
1211  }
+
1212  else {
+
1213  ship->SetDirectorInfo(Game::GetText("ai.seek-inbound"));
+
1214 
+
1215  // approach legs:
+
1216  if (inbound->Approach() > 0) {
+
1217  if (distance < 20 * self->Radius())
+ +
1219  }
+
1220 
+
1221  // marshall point and finals:
+
1222  else {
+
1223  if (inbound->Cleared() && distance < 10 * self->Radius()) {
+
1224  if (!inbound->Final()) {
+
1225  time_to_dock = TIME_TO_DOCK;
+
1226 
+
1227  FlightDeck* deck = inbound->GetDeck();
+
1228  if (deck) {
+
1229  double total_dist = Point(deck->EndPoint() - deck->StartPoint()).length();
+
1230  double current_dist = Point(deck->EndPoint() - ship->Location()).length();
+
1231 
+
1232  time_to_dock *= (current_dist / total_dist);
+
1233  }
+
1234 
+ +
1236  }
+
1237 
+
1238  inbound->SetFinal(true);
+
1239  ship->LowerGear();
+
1240  result.brake = 1;
+
1241  result.stop = 1;
+
1242  }
+
1243 
+
1244  else if (!inbound->Cleared() && distance < 2000) {
+
1245  ship->SetDirectorInfo(Game::GetText("ai.hold-final"));
+
1246  result = Steer();
+
1247  result.brake = 1;
+
1248  result.stop = 1;
+
1249  }
+
1250  }
+
1251  }
+
1252 
+
1253  return result;
+
1254  }
+
1255 
+
1256  else if (rtb_code) {
+
1257  return Seek(objective);
+
1258  }
+
1259 
+
1260  SimObject* tgt = target;
+
1261 
+
1262  if (ward && !tgt)
+
1263  tgt = ward;
+
1264 
+
1265  if (tgt && too_close == tgt->Identity()) {
+
1266  drop_time = 4;
+
1267  return Steer();
+
1268  }
+
1269 
+
1270  else if (navpt && navpt->Action() == Instruction::LAUNCH) {
+
1271  ship->SetDirectorInfo(Game::GetText("ai.launch"));
+
1272  return Seek(objective);
+
1273  }
+
1274 
+
1275  else if (farcaster) {
+
1276  // wingmen should
+
1277  if (element_index > 1)
+
1278  return SeekFormationSlot();
+
1279 
+
1280  ship->SetDirectorInfo(Game::GetText("ai.seek-farcaster"));
+
1281  return Seek(objective);
+
1282  }
+
1283 
+
1284  else if (drop_time > 0) {
+
1285  return Steer();
+
1286  }
+
1287 
+
1288  if (tgt) {
+
1289  double basis = self->Radius() + tgt->Radius();
+
1290  double gap = distance - basis;
+
1291 
+
1292  // target behind:
+
1293  if (objective.z < 0) {
+
1294  // leave some room for an attack run:
+
1295  if (gap < 8000) {
+
1296  Steer s;
+
1297 
+
1298  s.pitch = -0.1;
+
1299  if (objective.x > 0) s.yaw = 0.1;
+
1300  else s.yaw = -0.1;
+
1301 
+
1302  return s;
+
1303  }
+
1304 
+
1305  // start the attack run:
+
1306  else {
+
1307  return Seek(objective);
+
1308  }
+
1309  }
+
1310 
+
1311  // target in front:
+
1312  else {
+
1313  if (tgt->Type() == SimObject::SIM_SHIP) {
+
1314  Ship* tgt_ship = (Ship*) tgt;
+
1315 
+
1316  // capital target strike:
+
1317  if (tgt_ship->IsStatic()) {
+
1318  if (gap < 2500)
+
1319  return Flee(objective);
+
1320  }
+
1321 
+
1322  else if (tgt_ship->IsStarship()) {
+
1323  if (gap < 1000)
+
1324  return Flee(objective);
+
1325 
+
1326  else if (ship->GetFlightModel() == Ship::FM_STANDARD && gap < 20e3)
+
1327  go_manual = true;
+
1328  }
+
1329  }
+
1330 
+
1331  // fighter melee:
+
1332  if (tgt->Velocity() * ship->Velocity() < 0) {
+
1333  // head-to-head pass:
+
1334  if (gap < 1250)
+
1335  return Flee(objective);
+
1336  }
+
1337 
+
1338  else if (gap < 250) {
+
1339  return Steer();
+
1340  }
+
1341 
+
1342  ship->SetDirectorInfo(Game::GetText("ai.seek-target"));
+
1343  return Seek(objective);
+
1344  }
+
1345  }
+
1346 
+
1347  if (navpt) {
+
1348  ship->SetDirectorInfo(Game::GetText("ai.seek-navpt"));
+
1349  }
+
1350 
+
1351  return Seek(objective);
+
1352 }
+
1353 
+
1354 // +--------------------------------------------------------------------+
+
1355 
+
1356 Steer
+ +
1358 {
+
1359  Steer s;
+
1360 
+
1361  // advance memory pipeline:
+
1362  az[2] = az[1]; az[1] = az[0];
+
1363  el[2] = el[1]; el[1] = el[0];
+
1364 
+
1365  Element* elem = ship->GetElement();
+
1366  Ship* lead = elem->GetShip(1);
+
1367 
+
1368  if (lead) {
+
1369  SimRegion* self_rgn = ship->GetRegion();
+
1370  SimRegion* lead_rgn = lead->GetRegion();
+
1371 
+
1372  if (self_rgn != lead_rgn) {
+
1373  QuantumDrive* qdrive = ship->GetQuantumDrive();
+
1374  bool use_farcaster = !qdrive ||
+
1375  !qdrive->IsPowerOn() ||
+
1376  qdrive->Status() < System::DEGRADED;
+
1377 
+
1378  if (use_farcaster) {
+
1379  FindObjectiveFarcaster(self_rgn, lead_rgn);
+
1380  }
+
1381 
+
1382  else if (qdrive) {
+
1383  if (qdrive->ActiveState() == QuantumDrive::ACTIVE_READY) {
+
1384  qdrive->SetDestination(lead_rgn, lead->Location());
+
1385  qdrive->Engage();
+
1386  }
+
1387  }
+
1388  }
+
1389  }
+
1390 
+
1391  // do station keeping?
+
1392  if (distance < ship->Radius() * 10 && lead->Velocity().length() < 50) {
+
1393  distance = -1;
+
1394  return s;
+
1395  }
+
1396 
+
1397  // approach
+
1398  if (objective.z > ship->Radius() * -4) {
+
1399  az[0] = atan2(fabs(objective.x), objective.z) * 50;
+
1400  el[0] = atan2(fabs(objective.y), objective.z) * 50;
+
1401 
+
1402  if (objective.x < 0) az[0] = -az[0];
+
1403  if (objective.y > 0) el[0] = -el[0];
+
1404 
+
1405  s.yaw = az[0] - seek_damp * (az[1] + az[2] * 0.5);
+
1406  s.pitch = el[0] - seek_damp * (el[1] + el[2] * 0.5);
+
1407  }
+
1408 
+
1409  // reverse
+
1410  else {
+
1411  if (objective.x > 0) s.yaw = 1.0f;
+
1412  else s.yaw = -1.0f;
+
1413 
+
1414  s.pitch = -objective.y * 0.5f;
+
1415  }
+
1416 
+
1417  seeking = 1;
+
1418  ship->SetDirectorInfo(Game::GetText("ai.seek-formation"));
+
1419 
+
1420  return s;
+
1421 }
+
1422 
+
1423 // +--------------------------------------------------------------------+
+
1424 
+
1425 Steer
+
1426 FighterAI::Seek(const Point& point)
+
1427 {
+
1428  Steer s;
+
1429 
+
1430  // advance memory pipeline:
+
1431  az[2] = az[1]; az[1] = az[0];
+
1432  el[2] = el[1]; el[1] = el[0];
+
1433 
+
1434  // approach
+
1435  if (point.z > 0.0f) {
+
1436  az[0] = atan2(fabs(point.x), point.z) * seek_gain;
+
1437  el[0] = atan2(fabs(point.y), point.z) * seek_gain;
+
1438 
+
1439  if (point.x < 0) az[0] = -az[0];
+
1440  if (point.y > 0) el[0] = -el[0];
+
1441 
+
1442  s.yaw = az[0] - seek_damp * (az[1] + az[2] * 0.5);
+
1443  s.pitch = el[0] - seek_damp * (el[1] + el[2] * 0.5);
+
1444 
+
1445  // pull up:
+
1446  if (ship->IsAirborne() && point.y > 5e3)
+
1447  s.pitch = -1.0f;
+
1448  }
+
1449 
+
1450  // reverse
+
1451  else {
+
1452  if (ship->IsAirborne()) {
+
1453  // pull up:
+
1454  if (point.y > 5e3) {
+
1455  s.pitch = -1.0f;
+
1456  }
+
1457 
+
1458  // head down:
+
1459  else if (point.y < -5e3) {
+
1460  s.pitch = 1.0f;
+
1461  }
+
1462 
+
1463  // level turn:
+
1464  else {
+
1465  if (point.x > 0) s.yaw = 1.0f;
+
1466  else s.yaw = -1.0f;
+
1467 
+
1468  s.brake = 0.5f;
+
1469  }
+
1470  }
+
1471 
+
1472  else {
+
1473  if (point.x > 0) s.yaw = 1.0f;
+
1474  else s.yaw = -1.0f;
+
1475  }
+
1476  }
+
1477 
+
1478  seeking = 1;
+
1479 
+
1480  return s;
+
1481 }
+
1482 
+
1483 // +--------------------------------------------------------------------+
+
1484 
+
1485 Steer
+ +
1487 {
+
1488  // MISSILE THREAT REACTION:
+
1489  if (threat_missile) {
+
1490  evading = true;
+
1491  SetTarget(0);
+
1492  drop_time = 3 * (3-ai_level);
+
1493 
+
1494  // dropped a decoy for this missile yet?
+
1495  if (decoy_missile != threat_missile) {
+
1496  ship->FireDecoy();
+ +
1498  }
+
1499 
+
1500  // beam the missile
+
1501  ship->SetDirectorInfo(Game::GetText("ai.evade-missile"));
+
1502 
+
1503  Point beam_line = threat_missile->Velocity().cross(Point(0,1,0));
+
1504  beam_line.Normalize();
+
1505  beam_line *= 1e6;
+
1506 
+
1507  Point evade_p;
+
1508  Point evade_w1 = threat_missile->Location() + beam_line;
+
1509  Point evade_w2 = threat_missile->Location() - beam_line;
+
1510 
+
1511  double d1 = Point(evade_w1 - ship->Location()).length();
+
1512  double d2 = Point(evade_w2 - ship->Location()).length();
+
1513 
+
1514  if (d1 > d2)
+
1515  evade_p = Transform(evade_w1);
+
1516  else
+
1517  evade_p = Transform(evade_w2);
+
1518 
+
1519  return Seek(evade_p);
+
1520  }
+
1521 
+
1522  // GENERAL THREAT EVASION:
+
1523  if (threat && !form_up) {
+
1524  double threat_range = 20e3;
+
1525 
+
1526  Ship* threat_ship = (Ship*) threat;
+
1527  double threat_dist = Point(threat->Location() - ship->Location()).length();
+
1528 
+
1529  if (threat_ship->IsStarship()) {
+
1530  threat_range = CalcDefensePerimeter(threat_ship);
+
1531  }
+
1532 
+
1533  if (threat_dist <= threat_range) {
+
1534  ship->SetDirectorInfo(Game::GetText("ai.evade-threat"));
+
1535 
+
1536  if (ship->IsAirborne()) {
+
1537  evading = true;
+
1538  Point beam_line = threat->Velocity().cross(Point(0,1,0));
+
1539  beam_line.Normalize();
+
1540  beam_line *= threat_range;
+
1541 
+
1542  Point evade_w = threat->Location() + beam_line;
+
1543  Point evade_p = Transform(evade_w);
+
1544 
+
1545  return Seek(evade_p);
+
1546  }
+
1547 
+
1548  else if (threat_ship->IsStarship()) {
+
1549  evading = true;
+
1550 
+
1551  if (target == threat_ship && threat_dist < threat_range / 4) {
+
1552  SetTarget(0);
+
1553  drop_time = 5;
+
1554  }
+
1555 
+
1556  if (!target) {
+
1557  ship->SetDirectorInfo(Game::GetText("ai.evade-starship"));
+
1558 
+
1559  // flee for three seconds:
+
1560  if ((ship->MissionClock() & 3) != 3) {
+
1561  return Flee(Transform(threat->Location()));
+
1562  }
+
1563 
+
1564  // jink for one second:
+
1565  else {
+
1566  if (Game::GameTime() - jink_time > 1500) {
+ +
1568  jink = Point(rand() - 16384,
+
1569  rand() - 16384,
+
1570  rand() - 16384) * 15e3;
+
1571  }
+
1572 
+
1573  Point evade_w = ship->Location() + jink;
+
1574  Point evade_p = Transform(evade_w);
+
1575 
+
1576  return Seek(evade_p);
+
1577  }
+
1578  }
+
1579 
+
1580  else {
+
1581  ship->SetDirectorInfo(Game::GetText("ai.evade-and-seek"));
+
1582 
+
1583  // seek for three seconds:
+
1584  if ((ship->MissionClock() & 3) < 3) {
+
1585  return Steer(); // no evasion
+
1586  }
+
1587 
+
1588  // jink for one second:
+
1589  else {
+
1590  if (Game::GameTime() - jink_time > 1000) {
+ +
1592  jink = Point(rand() - 16384,
+
1593  rand() - 16384,
+
1594  rand() - 16384);
+
1595  }
+
1596 
+
1597  Point evade_w = target->Location() + jink;
+
1598  Point evade_p = Transform(evade_w);
+
1599 
+
1600  return Seek(evade_p);
+
1601  }
+
1602  }
+
1603  }
+
1604 
+
1605  else {
+
1606  evading = true;
+
1607 
+
1608  if (target == threat) {
+
1609  if (target->Type() == SimObject::SIM_SHIP) {
+
1610  Ship* tgt_ship = (Ship*) target;
+
1611  if (tgt_ship->GetTrigger(0)) {
+
1612  SetTarget(0);
+
1613  drop_time = 3;
+
1614  }
+
1615  }
+
1616  }
+
1617 
+
1618  else if (target && threat_dist < threat_range / 2) {
+
1619  SetTarget(0);
+
1620  drop_time = 3;
+
1621  }
+
1622 
+
1623  if (target)
+
1624  ship->SetDirectorInfo(Game::GetText("ai.evade-and-seek"));
+
1625  else
+
1626  ship->SetDirectorInfo(Game::GetText("ai.random-evade"));
+
1627 
+
1628  // beam the threat
+
1629  Point beam_line = threat->Velocity().cross(Point(0,1,0));
+
1630  beam_line.Normalize();
+
1631  beam_line *= 1e6;
+
1632 
+
1633  Point evade_p;
+
1634  Point evade_w1 = threat->Location() + beam_line;
+
1635  Point evade_w2 = threat->Location() - beam_line;
+
1636 
+
1637  double d1 = Point(evade_w1 - ship->Location()).length();
+
1638  double d2 = Point(evade_w2 - ship->Location()).length();
+
1639 
+
1640  if (d1 > d2)
+
1641  evade_p = Transform(evade_w1);
+
1642  else
+
1643  evade_p = Transform(evade_w2);
+
1644 
+
1645  if (!target) {
+
1646  DWORD jink_rate = 400 + 200 * (3-ai_level);
+
1647 
+
1648  if (Game::GameTime() - jink_time > jink_rate) {
+ +
1650  jink = Point(rand() - 16384,
+
1651  rand() - 16384,
+
1652  rand() - 16384) * 2000;
+
1653  }
+
1654 
+
1655  evade_p += jink;
+
1656  }
+
1657 
+
1658  Steer steer = Seek(evade_p);
+
1659 
+
1660  if (target)
+
1661  return steer / 4;
+
1662 
+
1663  return steer;
+
1664  }
+
1665  }
+
1666  }
+
1667 
+
1668  return Steer();
+
1669 }
+
1670 
+
1671 // +--------------------------------------------------------------------+
+
1672 
+
1673 void
+ +
1675 {
+
1676  // if nothing to shoot at, forget it:
+
1677  if (!target || target->Integrity() < 1)
+
1678  return;
+
1679 
+
1680  // if the objective is a navpt or landing bay (not a target), then don't shoot!
+ +
1682  return;
+
1683 
+
1684  // object behind us, or too close:
+
1685  if (objective.z < 0 || distance < 4 * self->Radius())
+
1686  return;
+
1687 
+
1688  // compute the firing cone:
+
1689  double cross_section = 2 * target->Radius() / distance;
+
1690  double gun_basket = cross_section * 2;
+
1691 
+
1692  Weapon* primary = ship->GetPrimary();
+
1693  Weapon* secondary = ship->GetSecondary();
+
1694  const WeaponDesign* dsgn_primary = 0;
+
1695  const WeaponDesign* dsgn_secondary = 0;
+
1696  bool use_primary = true;
+
1697  Ship* tgt_ship = 0;
+
1698 
+
1699  if (target->Type() == SimObject::SIM_SHIP) {
+
1700  tgt_ship = (Ship*) target;
+
1701 
+
1702  if (tgt_ship->InTransition())
+
1703  return;
+
1704  }
+
1705 
+
1706  if (primary) {
+
1707  dsgn_primary = primary->Design();
+
1708 
+
1709  if (dsgn_primary->aim_az_max > 5*DEGREES && distance > dsgn_primary->max_range/2)
+
1710  gun_basket = cross_section * 4;
+
1711 
+
1712  gun_basket *= (3-ai_level);
+
1713 
+
1714  if (tgt_ship) {
+
1715  if (!primary->CanTarget(tgt_ship->Class()))
+
1716  use_primary = false;
+
1717 
+
1718  /*** XXX NEED TO SUBTARGET SYSTEMS IF TARGET IS STARSHIP...
+
1719  else if (tgt_ship->ShieldStrength() > 10)
+
1720  use_primary = false;
+
1721  ***/
+
1722  }
+
1723 
+
1724  if (use_primary) {
+
1725  // is target in the basket?
+
1726  double dx = fabs(objective.x / distance);
+
1727  double dy = fabs(objective.y / distance);
+
1728 
+
1729  if (primary->GetFiringOrders() == Weapon::MANUAL &&
+
1730  dx < gun_basket && dy < gun_basket &&
+
1731  distance > dsgn_primary->min_range &&
+
1732  distance < dsgn_primary->max_range &&
+
1733  !primary->IsBlockedFriendly())
+
1734  {
+
1735  ship->FirePrimary();
+
1736  }
+
1737  }
+
1738  }
+
1739 
+
1740  if (secondary && secondary->GetFiringOrders() == Weapon::MANUAL) {
+
1741  dsgn_secondary = secondary->Design();
+
1742 
+
1743  if (missile_time <= 0 && secondary->Ammo() && !secondary->IsBlockedFriendly()) {
+
1744  if (secondary->Locked() || !dsgn_secondary->self_aiming) {
+
1745  // is target in basket?
+
1746  Point tgt = AimTransform(target->Location());
+
1747  double tgt_range = tgt.Normalize();
+
1748 
+
1749  int factor = 2-ai_level;
+
1750  double s_range = 0.5 + 0.2 * factor;
+
1751  double s_basket = 0.3 + 0.2 * factor;
+
1752  double extra_time = 10 * factor * factor + 5;
+
1753 
+
1754  if (!dsgn_secondary->self_aiming)
+
1755  s_basket *= 0.33;
+
1756 
+
1757  if (tgt_ship) {
+
1758  if (tgt_ship->Class() == Ship::MINE) {
+
1759  extra_time = 10;
+
1760  s_range = 0.75;
+
1761  }
+
1762 
+
1763  else if (!tgt_ship->IsDropship()) {
+
1764  extra_time = 0.5 * factor + 0.5;
+
1765  s_range = 0.9;
+
1766  }
+
1767  }
+
1768 
+
1769  // is target in decent range?
+
1770  if (tgt_range < secondary->Design()->max_range * s_range) {
+
1771  double dx = fabs(tgt.x);
+
1772  double dy = fabs(tgt.y);
+
1773 
+
1774  if (dx < s_basket && dy < s_basket && tgt.z > 0) {
+
1775  if (ship->FireSecondary()) {
+
1776  missile_time = secondary->Design()->salvo_delay + extra_time;
+
1777 
+
1778  if (Game::GameTime() - last_call_time > 6000) {
+
1779  // call fox:
+
1780  int call = RadioMessage::FOX_3; // A2A
+
1781 
+
1782  if (secondary->CanTarget(Ship::GROUND_UNITS)) // AGM
+
1783  call = RadioMessage::FOX_1;
+
1784 
+
1785  else if (secondary->CanTarget(Ship::DESTROYER)) // ASM
+
1786  call = RadioMessage::FOX_2;
+
1787 
+ + +
1790  }
+
1791  }
+
1792  }
+
1793  }
+
1794  }
+
1795  }
+
1796  }
+
1797 }
+
1798 
+
1799 // +--------------------------------------------------------------------+
+
1800 
+
1801 double
+ +
1803 {
+
1804  double perimeter = 15e3;
+
1805 
+
1806  if (starship) {
+
1807  ListIter<WeaponGroup> g_iter = starship->Weapons();
+
1808  while (++g_iter) {
+
1809  WeaponGroup* group = g_iter.value();
+
1810 
+
1811  ListIter<Weapon> w_iter = group->GetWeapons();
+
1812  while (++w_iter) {
+
1813  Weapon* weapon = w_iter.value();
+
1814 
+
1815  if (weapon->Ammo() &&
+
1816  weapon->GetTarget() == ship &&
+
1817  !weapon->IsBlockedFriendly()) {
+
1818 
+
1819  double range = weapon->Design()->max_range * 1.2;
+
1820  if (range > perimeter)
+
1821  perimeter = range;
+
1822  }
+
1823  }
+
1824  }
+
1825  }
+
1826 
+
1827  return perimeter;
+
1828 }
+
1829 
+
1830 
+
1831 
+
+
+ + + + -- cgit v1.1