Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
SteerAI.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: SteerAI.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  Steering (low-level) Artificial Intelligence class
13 */
14 
15 #include "MemDebug.h"
16 #include "SteerAI.h"
17 #include "SeekerAI.h"
18 #include "FighterAI.h"
19 #include "StarshipAI.h"
20 #include "GroundAI.h"
21 #include "System.h"
22 
23 #include "Game.h"
24 #include "Physical.h"
25 
26 // +----------------------------------------------------------------------+
27 
28 Steer Steer::operator+(const Steer& s) const
29 {
30  return Steer(yaw+s.yaw, pitch+s.pitch, roll+s.roll, (brake>s.brake)?brake:s.brake);
31 }
32 
33 Steer Steer::operator-(const Steer& s) const
34 {
35  return Steer(yaw-s.yaw, pitch-s.pitch, roll-s.roll, (brake<s.brake)?brake:s.brake);
36 }
37 
38 Steer Steer::operator*(double f) const
39 {
40  return Steer(yaw*f, pitch*f, roll*f, brake);
41 }
42 
43 Steer Steer::operator/(double f) const
44 {
45  return Steer(yaw/f, pitch/f, roll/f, brake);
46 }
47 
48 
50 {
51  yaw += s.yaw;
52  pitch += s.pitch;
53  roll += s.roll;
54 
55  if (s.brake > brake)
56  brake = s.brake;
57 
58  if (s.stop)
59  stop = 1;
60 
61  return *this;
62 }
63 
65 {
66  yaw -= s.yaw;
67  pitch -= s.pitch;
68  roll -= s.roll;
69 
70  if (s.brake < brake)
71  brake = s.brake;
72 
73  if (s.stop)
74  stop = 1;
75 
76  return *this;
77 }
78 
79 
80 double
82 {
83  return sqrt(yaw*yaw + pitch*pitch);
84 }
85 
86 // +--------------------------------------------------------------------+
87 
88 Director*
89 SteerAI::Create(SimObject* self, int type)
90 {
91  switch (type) {
92  case SEEKER: return new(__FILE__,__LINE__) SeekerAI(self);
93  break;
94 
95  case STARSHIP: return new(__FILE__,__LINE__) StarshipAI(self);
96  break;
97 
98  case GROUND: return new(__FILE__,__LINE__) GroundAI(self);
99  break;
100 
101  default:
102  case FIGHTER: return new(__FILE__,__LINE__) FighterAI(self);
103  break;
104  }
105 }
106 
107 // +----------------------------------------------------------------------+
108 
110 : self(ship),
111 target(0), subtarget(0), other(0), distance(0.0), evade_time(0),
112 objective(0.0f, 0.0f, 0.0f)
113 {
114  seek_gain = 20;
115  seek_damp = 0.5;
116 
117  for (int i = 0; i < 3; i++)
118  az[i] = el[i] = 0;
119 }
120 
121 
122 // +--------------------------------------------------------------------+
123 
125 { }
126 
127 // +--------------------------------------------------------------------+
128 
129 void
131 {
132  if (target != targ) {
133  target = targ;
134 
135  if (target)
136  Observe(target);
137  }
138 
139  subtarget = sub;
140 }
141 
142 void
143 SteerAI::DropTarget(double dtime)
144 {
145  SetTarget(0);
146 }
147 
148 // +--------------------------------------------------------------------+
149 
150 bool
152 {
153  if (obj == target) {
154  target = 0;
155  subtarget = 0;
156  }
157 
158  if (obj == other) {
159  other = 0;
160  }
161 
162  return SimObserver::Update(obj);
163 }
164 
165 const char*
167 {
168  static char name[64];
169  sprintf_s(name, "SteerAI(%s)", self->Name());
170  return name;
171 }
172 
173 // +--------------------------------------------------------------------+
174 
175 Point
177 {
178  if (self) {
179  if (target)
180  return self->Velocity() - target->Velocity();
181  else
182  return self->Velocity();
183  }
184 
185  return Point(1, 0, 0);
186 }
187 
188 void
190 {
191  if (!self || !target) return;
192 
193  Point cv = ClosingVelocity();
194  double cvl = cv.length();
195  double time = 0;
196 
197  if (cvl > 5) {
198  // distance from self to target:
199  distance = Point(target->Location() - self->Location()).length();
200 
201  // time to reach target:
202  time = distance / cvl;
203 
204  // where the target will be when we reach it:
205  Point run_vec = target->Velocity();
206  obj_w = target->Location() + (run_vec * time);
207  }
208 
209  else {
210  obj_w = target->Location();
211  }
212 
213  // subsystem offset:
214  if (subtarget) {
215  Point offset = target->Location() - subtarget->MountLocation();
216  obj_w -= offset;
217  }
218 
219  distance = Point(obj_w - self->Location()).length();
220 
221  if (cvl > 5)
222  time = distance / cvl;
223 
224  // where we will be when the target gets there:
225  Point self_dest = self->Location() + cv * time;
226  Point err = obj_w - self_dest;
227 
228  obj_w += err;
229 
230  // transform into camera coords:
233 
234  distance = Point(obj_w - self->Location()).length();
235 }
236 
237 Point
239 {
240  Point obj_t = pt - self->Location();
241  Point result;
242 
243  if (self->FlightPathYawAngle() != 0 || self->FlightPathPitchAngle() != 0) {
244  double az = self->FlightPathYawAngle();
245  double el = self->FlightPathPitchAngle();
246 
247  const double MAX_ANGLE = 15*DEGREES;
248  const double MIN_ANGLE = 3*DEGREES;
249 
250  if (az > MAX_ANGLE)
251  az = MAX_ANGLE;
252  else if (az < -MAX_ANGLE)
253  az = -MAX_ANGLE;
254  else if (az > MIN_ANGLE)
255  az = MIN_ANGLE + (az-MIN_ANGLE)/2;
256  else if (az < -MIN_ANGLE)
257  az = -MIN_ANGLE + (az+MIN_ANGLE)/2;
258 
259  if (el > MAX_ANGLE)
260  el = MAX_ANGLE;
261  else if (el < -MAX_ANGLE)
262  el = -MAX_ANGLE;
263  else if (el > MIN_ANGLE)
264  el = MIN_ANGLE + (el-MIN_ANGLE)/2;
265  else if (el < -MIN_ANGLE)
266  el = -MIN_ANGLE + (el+MIN_ANGLE)/2;
267 
268  Camera cam;
269  cam.Clone(self->Cam());
270  cam.Yaw(az);
271  cam.Pitch(-el);
272 
273  result = Point(obj_t * cam.vrt(),
274  obj_t * cam.vup(),
275  obj_t * cam.vpn());
276  }
277  else {
278  Camera& cam = (Camera&) self->Cam(); // cast away const
279 
280  result = Point(obj_t * cam.vrt(),
281  obj_t * cam.vup(),
282  obj_t * cam.vpn());
283  }
284 
285  return result;
286 }
287 
288 Point
290 {
291  Camera& cam = (Camera&) self->Cam(); // cast away const
292  Point obj_t = pt - self->Location();
293 
294  Point result = Point(obj_t * cam.vrt(),
295  obj_t * cam.vup(),
296  obj_t * cam.vpn());
297 
298  return result;
299 }
300 
301 // +--------------------------------------------------------------------+
302 
303 void
305 {
306  accumulator.Clear();
307  magnitude = 0;
308 }
309 
310 int
312 {
313  int overflow = 0;
314 
315  double mag = steer.Magnitude();
316 
317  if (magnitude + mag > 1) {
318  overflow = 1;
319  double scale = (1 - magnitude) / mag;
320 
321  accumulator += steer * scale;
322  magnitude = 1;
323 
324  if (seeking) {
325  az[0] *= scale;
326  el[0] *= scale;
327  seeking = 0;
328  }
329  }
330  else {
331  accumulator += steer;
332  magnitude += mag;
333  }
334 
335  return overflow;
336 }
337 
338 // +--------------------------------------------------------------------+
339 
340 Steer
341 SteerAI::Seek(const Point& point)
342 {
343  Steer s;
344 
345  // advance memory pipeline:
346  az[2] = az[1]; az[1] = az[0];
347  el[2] = el[1]; el[1] = el[0];
348 
349  // approach
350  if (point.z > 0.0f) {
351  az[0] = atan2(fabs(point.x), point.z) * seek_gain;
352  el[0] = atan2(fabs(point.y), point.z) * seek_gain;
353 
354  if (point.x < 0) az[0] = -az[0];
355  if (point.y > 0) el[0] = -el[0];
356 
357  s.yaw = az[0] - seek_damp * (az[1] + az[2] * 0.5);
358  s.pitch = el[0] - seek_damp * (el[1] + el[2] * 0.5);
359  }
360 
361  // reverse
362  else {
363  if (point.x > 0) s.yaw = 1.0f;
364  else s.yaw = -1.0f;
365 
366  s.pitch = -point.y * 0.5f;
367  }
368 
369  seeking = 1;
370 
371  return s;
372 }
373 
374 // +--------------------------------------------------------------------+
375 
376 Steer
378 {
379  Steer s;
380 
381  Point point = pt;
382  point.Normalize();
383 
384  // approach
385  if (point.z > 0.0f) {
386  if (point.x > 0) s.yaw = -1.0f;
387  else s.yaw = 1.0f;
388  }
389 
390  // flee
391  else {
392  s.yaw = -point.x;
393  s.pitch = point.y;
394  }
395 
396  return s;
397 }
398 
399 // +--------------------------------------------------------------------+
400 
401 Steer
402 SteerAI::Avoid(const Point& point, float radius)
403 {
404  Steer s;
405 
406  if (point.z > 0) {
407  double ax = radius - fabs(point.x);
408  double ay = radius - fabs(point.y);
409 
410  // go around?
411  if (ax < ay) {
412  s.yaw = atan2(ax, point.z) * seek_gain;
413  if (point.x > 0) s.yaw = -s.yaw;
414  }
415 
416  // go over/under:
417  else {
418  s.pitch = atan2(ay, point.z) * seek_gain;
419  if (point.y < 0) s.pitch = -s.pitch;
420  }
421  }
422 
423  return s;
424 }
425 
426 // +--------------------------------------------------------------------+
427 
428 Steer
429 SteerAI::Evade(const Point& point, const Point& vel)
430 {
431  Steer evade;
432 
433  if (Game::GameTime() - evade_time > 1250) {
435 
436  int direction = (rand()>>9) & 0x07;
437 
438  switch (direction) {
439  default:
440  case 0: evade.yaw = 0; evade.pitch = -0.5; break;
441  case 1: evade.yaw = 0; evade.pitch = -1.0; break;
442  case 2: evade.yaw = 1; evade.pitch = -0.3; break;
443  case 3: evade.yaw = 1; evade.pitch = -0.6; break;
444  case 4: evade.yaw = 1; evade.pitch = -1.0; break;
445  case 5: evade.yaw = -1; evade.pitch = -0.3; break;
446  case 6: evade.yaw = -1; evade.pitch = -0.6; break;
447  case 7: evade.yaw = -1; evade.pitch = -1.0; break;
448  }
449  }
450 
451  return evade;
452 }
453