1 /*
2     GL-117
3     Copyright 2001, 2002 Thomas A. Drexl aka heptargon
4 
5     This file is part of GL-117.
6 
7     GL-117 is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11 
12     GL-117 is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with GL-117; if not, write to the Free Software
19     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 */
21 
22 /* This file includes all AI objects instancing models. */
23 
24 #ifndef IS_AIOBJECT_H
25 
26 #include "aiobject.h"
27 #include "glland.h"
28 #include "main.h"
29 #include "mathtab.h"
30 
31 // disabled
net_write()32 int DynamicObj::net_write ()
33 {
34   net [0] = '.';
35   int z = 1;
36   memcpy (&net [z], &tl->x, sizeof (&tl->x));
37   z += sizeof (&tl->x);
38   memcpy (&net [z], &tl->y, sizeof (&tl->y));
39   z += sizeof (&tl->y);
40   memcpy (&net [z], &tl->z, sizeof (&tl->z));
41   z += sizeof (&tl->z);
42   memcpy (&net [z], &phi, sizeof (&phi));
43   z += sizeof (&phi);
44   memcpy (&net [z], &theta, sizeof (&theta));
45   z += sizeof (&theta);
46   memcpy (&net [z], &gamma, sizeof (&gamma));
47   z += sizeof (&gamma);
48   return z;
49 }
50 
51 // disabled
net_read()52 void DynamicObj::net_read ()
53 {
54   int z = 1;
55   memcpy (&tl->x, &net [z], sizeof (&tl->x));
56   z += sizeof (&tl->x);
57   memcpy (&tl->y, &net [z], sizeof (&tl->y));
58   z += sizeof (&tl->y);
59   memcpy (&tl->z, &net [z], sizeof (&tl->z));
60   z += sizeof (&tl->z);
61   memcpy (&phi, &net [z], sizeof (&phi));
62   z += sizeof (&phi);
63   memcpy (&theta, &net [z], sizeof (&theta));
64   z += sizeof (&theta);
65   memcpy (&gamma, &net [z], sizeof (&gamma));
66   z += sizeof (&gamma);
67 }
68 
activate()69 void DynamicObj::activate ()
70 {
71   active = true;
72   draw = true;
73 }
74 
deactivate()75 void DynamicObj::deactivate ()
76 {
77   active = false;
78   draw = false;
79 }
80 
dinit()81 void DynamicObj::dinit ()
82 {
83   rot->a = 90;
84   phi = 0; theta = 0; gamma = 180;
85   rectheta = 0;
86   tl->z = 0; tl->x = 0;
87   forcex = 0; forcez = 0; forcey = 0;
88   maxthrust = 0.3; braking = 0/*0.99*/; manoeverability = 0.5;
89   thrust = maxthrust; recthrust = thrust; recheight = 5.0;
90   ttl = -1;
91   shield = 0.01F; maxshield = 0.01F;
92   immunity = 0;
93   recgamma = 180;
94   id = CANNON1;
95   impact = 7;
96   source = NULL;
97   points = 0;
98   party = 0;
99   easymodel = 1; // easy model
100   elevatoreffect = 0;
101   ruddereffect = 0;
102   rolleffect = 0;
103   maxgamma = 70;
104   maxtheta = 90;
105   gamma = 180;
106   theta = 0;
107   explode = 0;
108   sink = 0;
109   nimbility = 1.0;
110   fighterkills = 0;
111   shipkills = 0;
112   tankkills = 0;
113   otherkills = 0;
114   killed = false;
115   realism = false;
116   accx = accy = accz = 0;
117 }
118 
DynamicObj()119 DynamicObj::DynamicObj ()
120 {
121   dinit ();
122 }
123 
DynamicObj(Space * space2,CModel * o2,float zoom2)124 DynamicObj::DynamicObj (Space *space2, CModel *o2, float zoom2)
125 {
126   this->space = space2;
127   o = o2;
128   zoom = zoom2;
129   dinit ();
130   space->addObject (this);
131 }
132 
thrustUp()133 void DynamicObj::thrustUp ()
134 {
135   recthrust += maxthrust / 12;
136   if (recthrust > maxthrust) recthrust = maxthrust;
137 }
138 
thrustDown()139 void DynamicObj::thrustDown ()
140 {
141   recthrust -= maxthrust / 12;
142   if (recthrust < maxthrust / 2) recthrust = maxthrust / 2;
143 }
144 
distance(DynamicObj * target)145 float DynamicObj::distance (DynamicObj *target)
146 {
147   float dx = target->tl->x - tl->x;
148   float dz = target->tl->z - tl->z;
149   float dy = target->tl->y - tl->y;
150   return sqrt (dx * dx + dz * dz + dy * dy);
151 }
152 
distanceXZ(DynamicObj * target)153 float DynamicObj::distanceXZ (DynamicObj *target)
154 {
155   float dx = target->tl->x - tl->x;
156   float dz = target->tl->z - tl->z;
157   return sqrt (dx * dx + dz * dz);
158 }
159 
160 // check whether the object is exploding or sinking and deactivate if necessary
checkExplosion(Uint32 dt)161 void DynamicObj::checkExplosion (Uint32 dt)
162 {
163   if (explode > 0)
164   {
165     if (explode == 1)
166     {
167       ttl = -1;
168       if (id == STATIC_CONTAINER1 || id == STATIC_RADAR1 || id == STATIC_COMPLEX1)
169       {
170         setExplosion (1.5, 40 * timestep);
171         setBlackSmoke (3.0, 80 * timestep);
172       }
173       else if (id == STATIC_OILRIG1)
174       {
175         setExplosion (3.0, 40 * timestep);
176         setBlackSmoke (5.5, 80 * timestep);
177       }
178       else if (id == STATIC_TENT1)
179       {
180       }
181       else if (id == TANK1)
182       {
183       }
184       else
185       {
186         float zoom2 = zoom * 2;
187         if (zoom2 > 2) zoom2 = 2;
188         setExplosion (zoom2, 35 * timestep);
189         setBlackSmoke (1.0, 60 * timestep);
190       }
191     }
192     if (id >= STATIC_GROUND || (id >= MOVING_GROUND && id <= MOVING_WATER))
193     {
194       if (explode >= 25 * timestep && ttl == -1)
195       {
196         setExplosion (zoom * 2, 35 * timestep);
197         setBlackSmoke (1.0, 60 * timestep);
198         ttl = -2;
199       }
200       if (explode >= 30 * timestep && ttl == -2)
201       {
202         setExplosion (zoom * 2, 35 * timestep);
203         setBlackSmoke (1.0, 60 * timestep);
204         ttl = -3;
205       }
206     }
207     if (explode >= 35 * timestep)
208     {
209       deactivate ();
210       ttl = -1;
211       explode += dt; // must be > 35*timestep to end mission
212       if (id >= STATIC_GROUND || (id >= MOVING_GROUND && id <= MOVING_WATER))
213       {
214         explode = 0;
215         draw = true;
216         id = STATIC_PASSIVE;
217         shield = 100000;
218         o = &model_rubble1;
219         zoom *= 0.7F;
220         if (zoom > 1) zoom = 1;
221         tl->y = l->getExactHeight (tl->x, tl->z) + zoom / 4;
222       }
223     }
224     else
225     {
226       explode += dt;
227     }
228   }
229   if (sink)
230   {
231     sink += dt;
232     if (sink > 100 * timestep)
233     {
234       deactivate (); ttl = -1;
235     }
236   }
237 }
238 
239 // check the objects shield value and explode/sink if necessary
checkShield()240 void DynamicObj::checkShield ()
241 {
242   if (shield <= 0)
243   {
244     shield = 0;
245     if (explode <= 0)
246     {
247       if (id >= MISSILE1 && id <= MISSILE2)
248       { explode = 1; active = false; }
249       if (id >= FIGHTER1 && id <= FIGHTER2)
250       { explode = 1; active = false; }
251       if (id >= TANK1 && id <= TANK2)
252       { explode = 1; active = false; }
253       if (id >= FLAK1 && id <= FLAK2)
254       { explode = 1; active = false; }
255       if (id >= STATIC_PASSIVE)
256       { explode = 1; active = false; }
257     }
258     if (sink <= 0)
259       if (id >= SHIP1 && id <= SHIP2)
260       { sink = 1; }
261   }
262 }
263 
264 // check whether the object collides on the ground and alter gamma and y-translation
crashGround(Uint32 dt)265 void DynamicObj::crashGround (Uint32 dt)
266 {
267   if (id >= MOVING_GROUND)
268     return;
269   float height = tl->y - l->getExactHeight (tl->x, tl->z);
270   if (height < zoom)
271   {
272     tl->y -= (height - zoom);
273     gamma += 10;
274     if (shield > 0)
275     {
276       if (id >= MISSILE1 && id <= MISSILE2)
277       {
278         setExplosion (1.2, 30 * timestep);
279         setBlackSmoke (1.2, 30 * timestep);
280       }
281       if (id >= FIGHTER1 && id <= FIGHTER2)
282       {
283         setExplosion (0.2, 25 * timestep);
284         setBlackSmoke (0.5, 25 * timestep);
285       }
286     }
287     if (id >= CANNON1 && id <= CANNON2)
288       deactivate ();
289     float decfac = 3.0F;
290     if (this == (DynamicObj *) fplayer && game == GAME_PLAY)
291     {
292       if (difficulty == 1) decfac = 6.0F;
293       else if (difficulty == 2) decfac = 15.0F;
294     }
295     if (realism && this == (DynamicObj *) fplayer && game == GAME_PLAY)
296 	  shield = -1;
297 	else
298 	  shield -= decfac * dt / timestep;
299   }
300   // restrict to a maximum height, we want an action game!!! a little bit more now 50 -> 80
301   if (height > 80) tl->y = l->getHeight (tl->x, tl->z) + 80;
302 }
303 
304 // check for collision, simplified model, each model is surrounded by a cube
305 // this works pretty well, but we must use more than one model for complex models or scenes
collide(DynamicObj * d,Uint32 dt)306 void DynamicObj::collide (DynamicObj *d, Uint32 dt) // d must be the medium (laser, missile)
307 {
308   if (immunity > 0 || d->immunity > 0) return;
309   if (explode > 0 || sink > 0) return;
310 
311   bool collide = false;
312   if (tl->x + o->cubex >= d->tl->x - d->o->cubex && tl->x - o->cubex <= d->tl->x + d->o->cubex &&
313       tl->y + o->cubey >= d->tl->y - d->o->cubey && tl->y - o->cubey <= d->tl->y + d->o->cubey &&
314       tl->z + o->cubez >= d->tl->z - d->o->cubez && tl->z - o->cubez <= d->tl->z + d->o->cubez)
315   {
316     collide = true;
317   }
318 
319   if (collide)
320   {
321     if (this == (DynamicObj *) fplayer && game == GAME_PLAY && realism && d->id >= AIR && d->id < MOVING_GROUND)
322 	  {
323       shield = -1.0F; // player collision vs another plane in SIM mode, boom
324 	    d->shield = -1.0F;
325 	  }
326     if (id < STATIC_PASSIVE || (id >= STATIC_PASSIVE && d->id >= MISSILE1 && d->id <= MISSILE2))
327       shield -= (float) d->impact;
328     else
329       shield -= 2.0F;
330     d->shield -= (float) impact;
331     if (d->source != NULL && active) // only for missiles/cannons
332     {
333       if (d->source->party != party) // calculate points
334       {
335         if (maxshield < 2000)
336           d->source->points += (int) impact; // extra points for shooting an enemy object
337       }
338       else
339       {
340         d->source->points -= (int) impact; // subtract points for shooting an own object
341       }
342 
343       if (shield <= 0)
344         if (d->source->party != party && active && draw && !killed)
345           if (d->source->id >= FIGHTER1 && d->source->id <= FIGHTER2)
346           {
347             killed = true;
348             if (id >= FIGHTER1 && id <= FIGHTER2)
349               d->source->fighterkills ++;
350             else if (id >= SHIP1 && id <= SHIP2)
351               d->source->shipkills ++;
352             else if ((id >= FLAK1 && id <= FLAK2) || (id >= TANK1 && id <= TANK2))
353               d->source->tankkills ++;
354             else
355               d->source->otherkills ++;
356           }
357     }
358     setExplosion (0.2, 20 * timestep);
359     setBlackSmoke (0.5, 30 * timestep);
360   }
361 }
362 
setExplosion(float maxzoom,int len)363 void DynamicObj::setExplosion (float maxzoom, int len)
364 {
365   int i;
366   for (i = 0; i < maxexplosion; i ++) // search a free explosion instance
367     if (explosion [i]->ttl <= 0)
368       break;
369   if (i >= maxexplosion) i = 0;
370   explosion [i]->setExplosion (tl->x, tl->y, tl->z, forcex, forcey, forcez, maxzoom, len);
371 }
372 
setBlackSmoke(float maxzoom,int len)373 void DynamicObj::setBlackSmoke (float maxzoom, int len)
374 {
375   int i;
376   for (i = 0; i < maxblacksmoke; i ++) // search a free blacksmoke instance
377     if (blacksmoke [i]->ttl <= 0)
378       break;
379   if (i >= maxblacksmoke) i = 0;
380   blacksmoke [i]->setBlackSmoke (tl->x, tl->y, tl->z, phi, maxzoom, len);
381 }
382 
383 // return heading difference towards enemy
getAngle(DynamicObj * o)384 int DynamicObj::getAngle (DynamicObj *o)
385 {
386   float dx2 = o->tl->x - tl->x, dz2 = o->tl->z - tl->z;
387   int a, w = (int) phi;
388   if (dz2 > -0.0001 && dz2 < 0.0001) dz2 = 0.0001;
389 
390   a = (int) (atan (dx2 / dz2) * 180 / PI);
391   if (dz2 > 0)
392   {
393     if (dx2 > 0) a -= 180;
394     else a += 180;
395   }
396   int aw = a - w;
397   if (aw < -180) aw += 360;
398   if (aw > 180) aw -= 360;
399   return aw;
400 }
401 
402 // return elevation difference towards enemy
getAngleH(DynamicObj * o)403 int DynamicObj::getAngleH (DynamicObj *o)
404 {
405   float disttarget = distance (o);
406   return (int) (atan ((o->tl->y - tl->y) / disttarget) * 180 / PI - (gamma - 180));
407 }
408 
409 // check for a looping, this is tricky :-)
checkLooping()410 bool DynamicObj::checkLooping ()
411 {
412   if (gamma > 270)
413   {
414     gamma = 540 - gamma;
415     theta += 180;
416     phi += 180;
417     rectheta += 180;
418     if (theta >= 360) theta -= 360;
419     if (rectheta >= 360) rectheta -= 360;
420     if (phi >= 360) phi -= 360;
421     return true;
422   }
423   else if (gamma < 90)
424   {
425     gamma = 180 - gamma;
426     theta += 180;
427     phi += 180;
428     rectheta += 180;
429     if (theta >= 360) theta -= 360;
430     if (rectheta >= 360) rectheta -= 360;
431     if (phi >= 360) phi -= 360;
432     return true;
433   }
434   return false;
435 }
436 
437 // discrete movement, called about timestep times per second, timer-dependant, currently without extra thread (GLUT)!
move(Uint32 dt)438 void DynamicObj::move (Uint32 dt)
439 {
440   if (dt <= 0) return;
441   if (realspeed <= 0) realspeed = 1.0F;
442 
443   float brakepower = 1.0F;
444   float timefac = (float) dt / (float) timestep;
445 
446   checkExplosion (dt); // check if this object is exploding
447   if (sink > 0) // only ships (they will not explode)
448   {
449     tl->y -= 0.02 * timefac; // sink down
450     gamma = recgamma = 180.0 + 0.5 * (float) sink / timestep; // change angle when sinking
451     return; // and exit move()
452   }
453   if (!active && !draw) return; // exit if not active
454 
455   if (id >= STATIC_PASSIVE) // only buildings, static objects
456   {
457     if (id == STATIC_TENT1) theta = 178;
458     // set the correct angles to diplay
459     rot->setAngles ((short) (90 + gamma - 180), (short) theta + 180, (short) -phi);
460     checkShield ();
461     return; // and exit this function
462   }
463 
464   if (id == FLARE1) // only flares
465   {
466     tl->y -= 0.04 * timefac; // fall down (gravity, constant)
467     zoom = 0.12F + 0.03F * sin ((float) ttl / (float) timestep / 15); // blink (high frequency)
468     phi = camphi; // angles to viewer (player)
469     theta = 0;
470     gamma = camgamma;
471   }
472 
473   if (id == CHAFF1) // only chaff
474   {
475     tl->y -= 0.04 * timefac; // fall down (gravity, constant)
476     zoom = 0.12F + 0.01F * (80 * timestep - ttl) / timestep; // spread out
477     phi = camphi; // angles to viewer (player)
478     theta = 0;
479     gamma = camgamma;
480   }
481 
482   // check maximum gamma
483   if (easymodel == 1)
484   {
485     if (gamma > 180 + maxgamma) gamma = 180 + maxgamma;
486     else if (gamma < 180 - maxgamma) gamma = 180 - maxgamma;
487   }
488   else if (easymodel == 2) // otherwise check for value overflow due to loops
489   {
490     (void) checkLooping ();
491   }
492 
493   // the core of directional alterations and force calculations:
494   // easymodel==1 means to change heading due to roll angle
495   // this may seem complete nonsense for fighters, but it is just a simplification!!!
496   // angle / aileron = constant, thats enough for a simple AI
497   if (easymodel == 1)
498   {
499     if (id >= MOVING_GROUND)
500     {
501       phi += SIN(theta) * manoeverability * 667 * timefac; //10.0 * maxthrust / div;
502     }
503     else
504     {
505       phi += SIN(theta) * manoeverability * (3.33 + 15.0 * realspeed) * timefac;
506       gamma -= fabs (SIN(theta) * COS(gamma) / realspeed / 20) * timefac; // realistic modification
507       if (gamma < 180 - maxgamma) gamma = 180 - maxgamma;
508     }
509   }
510   else if (easymodel == 2) // now this is much more general, however simplified:
511   {
512     int vz = 1;
513     if (gamma < 90 || gamma > 270)
514       vz = -1;
515     // change heading and elevation due to ailerons and rudder
516     if (maxthrust + thrust <= -0.00001 || maxthrust + thrust >= 0.00001)
517     {
518       phi += vz * SIN(theta) * elevatoreffect * manoeverability * (3.33 + 15.0 * realspeed) * timefac;
519       gamma += COS(theta) * elevatoreffect * manoeverability * (3.33 + 15.0 * realspeed) * timefac;
520       phi += -vz * COS(theta) * ruddereffect * manoeverability * (0.66 + 3.0 * realspeed) * timefac;
521       gamma += SIN(theta) * ruddereffect * manoeverability * (0.66 + 3.0 * realspeed) * timefac;
522       if (!realism)
523         gamma -= fabs (SIN(theta) * COS(gamma) / realspeed / 20) * timefac; // realistic modification
524     }
525     // change roll due to roll ;-)
526     if (rolleffect)
527     {
528       theta += rolleffect * (nimbility * (1.0 + realspeed)) * timefac * 5.0F;
529       rectheta = theta;
530     }
531   }
532   if (phi < 0) phi += 360.0; // validate heading
533   else if (phi >= 360.0) phi -= 360.0;
534 
535   if (easymodel == 1) // easy model restrictions
536   {
537     if (rectheta > maxtheta) rectheta = maxtheta;
538     else if (rectheta < -maxtheta) rectheta = -maxtheta;
539     if (recgamma > 180 + maxgamma) recgamma = 180 + maxgamma;
540     else if (recgamma < 180 - maxgamma) recgamma = 180 - maxgamma;
541   }
542   else if (easymodel == 2)
543   {
544     if (theta < -180 && rectheta < -180)
545     { rectheta += 360; theta += 360; }
546     else if (theta >= 180 && rectheta >= 180)
547     { rectheta -= 360; theta -= 360; }
548   }
549 
550   if (recthrust > maxthrust) // check maximum throttle
551     recthrust = maxthrust;
552 
553   float throttlechange = maxthrust / 200 * timefac;
554   if (recthrust > thrust + throttlechange) // alter throttle effect slowly
555   {
556     thrust += throttlechange;
557   }
558   else if (recthrust < thrust - throttlechange)
559   {
560     thrust -= throttlechange;
561   }
562 
563   // PHYSICS (simplified model)
564 
565   CVector3 vaxis, uaxis, utemp, utemp2, utemp3;
566   float gammaup, phiup, thetaup;
567 
568   bool stop;
569   float gravityforce;
570 
571   if (id <= CANNON2)
572   {
573     tl->x += forcex * timefac; // add our vector to the translation
574     tl->z += forcez * timefac;
575     tl->y += forcey * timefac;
576     goto cannondone; // jump down to decrease ttl and test collision
577   }
578 
579   // axis pointing through the fighter's nose
580   vaxis.set (COS(gamma) * SIN(phi), SIN(gamma), COS(gamma) * COS(phi));
581 
582   if (realism)
583   {
584 
585     // axis pointing upwards through the cockpit
586     gammaup = gamma + 90;
587     thetaup = -theta;
588     phiup = phi;
589     uaxis.set (COS(gammaup) * SIN(phiup), SIN(gammaup), COS(gammaup) * COS(phiup)); // upward axis (theta = 0)
590     // now rotate around vaxis using theta
591     utemp.take (&uaxis);
592     utemp.mul (COS(thetaup));
593     utemp2.take (&vaxis);
594     utemp2.mul ((1 - COS(thetaup)) * uaxis.dotproduct (&vaxis));
595     utemp3.take (&uaxis);
596     utemp3.crossproduct (&vaxis);
597     utemp3.mul (SIN(thetaup));
598     utemp.add (&utemp2);
599     utemp.add (&utemp3);
600     uaxis.take (&utemp);
601 
602   }
603 
604   realspeed = sqrt (forcex * forcex + forcez * forcez + forcey * forcey);
605 
606   if (realism) // sim model
607   {
608 
609     // add drag force
610     braking = (fabs (ruddereffect) + fabs (elevatoreffect) * 4 + fabs (rolleffect)) * realspeed / 50;
611     brakepower = pow (0.93 - braking, timefac);
612 
613     accx *= brakepower;
614     accy *= brakepower;
615     accz *= brakepower;
616 
617     // add throttle force
618     accz += thrust * vaxis.z * 0.3 * timefac;
619     accx += thrust * vaxis.x * 0.3 * timefac;
620     accy -= thrust * vaxis.y * 0.3 * timefac;
621     // add elevation force
622     accz += thrust * uaxis.z * 0.067 * timefac;
623     accx += thrust * uaxis.x * 0.067 * timefac;
624     accy -= thrust * uaxis.y * 0.067 * timefac;
625     // add gravity force
626     accy -= 0.015 * timefac;
627     // add our vector to the translation
628     float stepfac = 0.24;
629     tl->x += accx * timefac * stepfac;
630     tl->z += accz * timefac * stepfac;
631     tl->y += accy * timefac * stepfac;
632     float scalef = 1.1;
633     forcex = accx * stepfac * scalef;
634     forcey = accy * stepfac * scalef;
635     forcez = accz * stepfac * scalef;
636 
637   }
638   else // action model
639   {
640 
641     // and correct the speedvector
642 
643     forcez = vaxis.z * realspeed;
644     forcex = vaxis.x * realspeed;
645     forcey = -vaxis.y * realspeed;
646 
647     // add throttle force
648 
649     forcez += thrust * vaxis.z * 0.01 * timefac; //0.03 and braking=0.97 by try and error
650     forcex += thrust * vaxis.x * 0.01 * timefac;
651     forcey -= thrust * vaxis.y * 0.01 * timefac;
652 
653     gravityforce = sqrt (realspeed) * vaxis.y * 0.0012 * timefac;
654     forcez += gravityforce * vaxis.z;
655     forcex += gravityforce * vaxis.x;
656     forcey -= gravityforce * vaxis.y;
657 
658     // drag force simulated by adjusting the vector
659 
660     if (easymodel == 2)
661       braking = (fabs (ruddereffect) + fabs (elevatoreffect)) * realspeed / 50;
662     else
663       braking = (fabs (theta / 45)) * realspeed / 50;
664     brakepower = pow (0.9915 - braking, timefac);
665     forcex *= brakepower; forcez *= brakepower; forcey *= brakepower;
666 
667   }
668 
669   stop = false;
670   if (id >= TANK1 && id <= TANK2) // tanks cannot climb steep faces
671   {
672     float newy = l->getExactHeight (tl->x + forcex, tl->z + forcez) + zoom / 2;
673     if (fabs (newy - tl->y) > 0.05)
674       stop = true;
675     else if (fabs (newy - tl->y) > 2)
676       stop = false;
677   }
678 
679   if (!realism)
680     if (!stop)
681     {
682       tl->x += forcex * timefac; // add our vector to the translation
683       tl->z += forcez * timefac;
684       tl->y += forcey * timefac;
685     }
686 
687   // calculate the objects real thrust only once
688   realspeed = sqrt (forcex * forcex + forcez * forcez + forcey * forcey);
689 
690   // objects moving on the ground should always change their elevation due to the surface
691   if (id >= TANK1 && id <= TANK2 && thrust > 0 && !stop)
692   {
693     float newy = l->getExactHeight (tl->x, tl->z) + zoom / 2;
694     float dy = newy - tl->y + forcey;
695     float dx = fabs (forcex) + fabs (forcez);
696     float gamma2 = 0;
697     if (fabs (dx) > 0.0001)
698       gamma2 = atan (dy / dx);
699     gamma = 180.0 + 180.0 / PI * gamma2;
700     tl->y = newy;
701   }
702 
703   if (id != ASTEROID)
704   {
705     // set angles to correctly display the object
706     rot->setAngles ((short) (90 + gamma - 180), (short) theta + 180, (short) -phi);
707   }
708   else // asteroids should rotate around their center of weight, as we must not change theta/gamma, we do this here
709   {
710     ttl -= dt; // we use the ttl value as timer, for other methods ttl<0 is the same as ttl=-1
711     if (ttl <= -360 * timestep) ttl = -1;
712     int rot1 = (int) (sin ((zoom - 1.3) * 8) * 4);
713     int rot2 = (int) (cos ((zoom - 1.3) * 8) * 4);
714     rot->setAngles ((short) (90 + gamma + ttl * rot1 / timestep - 180), (short) theta + ttl * rot2 / timestep + 180, (short) -phi);
715   }
716 
717 cannondone:;
718   if (ttl > 0)
719   {
720     ttl -= dt; // decrease time to live
721     if (ttl <= 0)
722     {
723       ttl = -1;
724       if (id >= MISSILE1 && id <= MISSILE2) recheight = -10; // missiles drop
725       else deactivate (); // cannon shots vanish
726     }
727   }
728   checkShield (); // check shield issues
729   crashGround (dt); // check ground collision
730   if (immunity > 0) immunity -= dt; // decrease immunity
731 }
732 
733 
734 
aiinit()735 void AIObj::aiinit ()
736 {
737   int i;
738   acttype = 0;
739   dualshot = false;
740   intelligence = 100;
741   aggressivity = 100;
742   precision = 100;
743   shield = 0.01F;
744   ai = true;
745   active = true;
746   draw = true;
747   target = NULL;
748   dtheta = 0;
749   dgamma = 0;
750   id = MISSILE1;
751   impact = 30;
752   manoevertheta = 0;
753   manoeverheight = 0;
754   manoeverthrust = 0;
755   idle = 0;
756   smokettl = 0;
757   firecannonttl = 0;
758   firemissilettl = 0;
759   fireflarettl = 0;
760   firechaffttl = 0;
761   flares = 0;
762   aw = 0;
763   source = NULL;
764   points = 0;
765   easymodel = 1;
766   elevatoreffect = 0;
767   ruddereffect = 0;
768   gamma = 180;
769   recgamma = 180;
770   dgamma = 0;
771   theta = 0;
772   maxgamma = 70;
773   maxtheta = 90;
774   missiletype = 0;
775   autofire = false;
776   ttl = -1;
777   ttf = 30 * timestep;
778   score = -1;
779   for (i = 0; i < missiletypes; i ++)
780     missiles [i] = 0;
781   for (i = 0; i < missileracks; i ++)
782   {
783     missilerack [i] = -1;
784     missilerackn [i] = 0;
785   }
786   bomber = 0;
787   timer = 0;
788   ammo = -1;
789   manoeverstate = 0;
790 }
791 
missileCount()792 void AIObj::missileCount ()
793 {
794   if (id < FIGHTER1 || id > FIGHTER2) return;
795 
796   int i;
797   for (i = 0; i < missiletypes; i ++)
798     missiles [i] = 0;
799   for (i = 0; i < missileracks; i ++)
800   {
801     if (missilerackn [i] > 0)
802     {
803       missiles [missilerack [i]] += missilerackn [i];
804     }
805   }
806 }
807 
newinit(int id,int party,int intelligence,int precision,int aggressivity)808 void AIObj::newinit (int id, int party, int intelligence, int precision, int aggressivity)
809 {
810   int i;
811   ai = true;
812   this->id = id;
813   this->party = party;
814   manoeverstate = 0;
815   activate ();
816   for (i = 0; i < missileracks; i ++)
817     missilerackn [i] = 0;
818   ammo = -1;
819   bomber = 0;
820   dualshot = false;
821   float cubefac = 0.6F; // fighter
822   float cubefac1 = 0.7F; // tanks and sams
823   o = getModel (id);
824   o->cubex = zoom; o->cubey = zoom; o->cubez = zoom;
825 
826   if (id == FIGHTER_FALCON)
827   {
828     maxthrust = 0.31;
829     nimbility = 0.86;
830     manoeverability = 0.48;
831     maxshield = 85;
832     zoom = 0.35;
833     maxtheta = 90.0;
834     maxgamma = 70.0;
835     missilerackn [0] = 2; missilerackn [1] = 2; missilerackn [2] = 2; missilerackn [3] = 2;
836     missilerack [0] = 0; missilerack [1] = 6; missilerack [2] = 6; missilerack [3] = 0;
837     flares = 20;
838     chaffs = 20;
839     statfirepower = 1;
840     ammo = 1200;
841   }
842   else if (id == FIGHTER_SWALLOW)
843   {
844     maxthrust = 0.24;
845     nimbility = 0.64;
846     manoeverability = 0.35;
847     maxshield = 110;
848     zoom = 0.43;
849     maxtheta = 90.0;
850     maxgamma = 70.0;
851     missilerackn [0] = 2; missilerackn [1] = 3; missilerackn [2] = 3; missilerackn [3] = 2;
852     missilerack [0] = 6; missilerack [1] = 3; missilerack [2] = 3; missilerack [3] = 6;
853     flares = 20;
854     chaffs = 20;
855     bomber = 1;
856     statfirepower = 3;
857     ammo = 1200;
858   }
859   else if (id == FIGHTER_HAWK)
860   {
861     maxthrust = 0.26;
862     nimbility = 0.72;
863     manoeverability = 0.42;
864     maxshield = 120;
865     zoom = 0.43;
866     maxtheta = 90.0;
867     maxgamma = 70.0;
868     missilerackn [0] = 1; missilerackn [1] = 3; missilerackn [2] = 3; missilerackn [3] = 1;
869     missilerack [0] = 6; missilerack [1] = 3; missilerack [2] = 3; missilerack [3] = 6;
870     flares = 20;
871     chaffs = 20;
872     bomber = 1;
873     statfirepower = 2;
874     ammo = 1200;
875   }
876   else if (id == FIGHTER_HAWK2)
877   {
878     maxthrust = 0.28;
879     nimbility = 0.75;
880     manoeverability = 0.44;
881     maxshield = 140;
882     zoom = 0.45;
883     maxtheta = 90.0;
884     maxgamma = 70.0;
885     missilerackn [0] = 1; missilerackn [1] = 3; missilerackn [2] = 3; missilerackn [3] = 1;
886     missilerack [0] = 6; missilerack [1] = 4; missilerack [2] = 4; missilerack [3] = 6;
887     flares = 20;
888     chaffs = 20;
889     bomber = 1;
890     statfirepower = 3;
891     ammo = 1400;
892     dualshot = true;
893   }
894   else if (id == FIGHTER_TRANSPORT)
895   {
896     maxthrust = 0.14;
897     maxshield = 45;
898     missiles [0] = 0;
899     nimbility = 0.15;
900     manoeverability = 0.05;
901     impact = 5;
902     zoom = 1.5;
903     maxgamma = 25;
904     maxtheta = 30;
905     flares = 0;
906     chaffs = 0;
907     ammo = 0;
908   }
909   else if (id == FIGHTER_TRANSPORT2)
910   {
911     maxthrust = 0.16;
912     maxshield = 35;
913     missiles [0] = 0;
914     nimbility = 0.12;
915     manoeverability = 0.04;
916     impact = 5;
917     zoom = 1.5;
918     maxgamma = 25;
919     maxtheta = 30;
920     flares = 0;
921     chaffs = 0;
922     ammo = 0;
923   }
924   else if (id == FIGHTER_BUZZARD)
925   {
926     maxthrust = 0.31;
927     nimbility = 0.82;
928     manoeverability = 0.46;
929     maxshield = 75;
930     zoom = 0.44;
931     maxtheta = 90.0;
932     maxgamma = 70.0;
933     missilerackn [0] = 2; missilerackn [1] = 2; missilerackn [2] = 2; missilerackn [3] = 2;
934     missilerack [0] = 0; missilerack [1] = 6; missilerack [2] = 6; missilerack [3] = 0;
935     flares = 20;
936     chaffs = 20;
937     statfirepower = 2;
938     ammo = 1200;
939   }
940   else if (id == FIGHTER_CROW)
941   {
942     maxthrust = 0.25;
943     nimbility = 0.72;
944     manoeverability = 0.4;
945     maxshield = 60;
946     zoom = 0.41;
947     maxtheta = 90.0;
948     maxgamma = 70.0;
949     missilerackn [0] = 1; missilerackn [1] = 2; missilerackn [2] = 2; missilerackn [3] = 1;
950     missilerack [0] = 6; missilerack [1] = 0; missilerack [2] = 0; missilerack [3] = 6;
951     flares = 20;
952     chaffs = 20;
953     statfirepower = 1;
954     ammo = 1000;
955   }
956   else if (id == FIGHTER_STORM)
957   {
958     maxthrust = 0.25;
959     nimbility = 0.52;
960     manoeverability = 0.34;
961     maxshield = 160;
962     zoom = 0.45;
963     maxtheta = 90.0;
964     maxgamma = 70.0;
965     missilerackn [0] = 1; missilerackn [1] = 2; missilerackn [2] = 2; missilerackn [3] = 1;
966     missilerack [0] = 6; missilerack [1] = 0; missilerack [2] = 0; missilerack [3] = 6;
967     flares = 25;
968     chaffs = 25;
969     statfirepower = 4;
970     ammo = 1800;
971   }
972   else if (id == FIGHTER_PHOENIX)
973   {
974     maxthrust = 0.3;
975     nimbility = 0.54;
976     manoeverability = 0.34;
977     maxshield = 180;
978     zoom = 0.47;
979     maxtheta = 90.0;
980     maxgamma = 70.0;
981     missilerackn [0] = 3; missilerackn [1] = 3; missilerackn [2] = 3; missilerackn [3] = 3;
982     missilerack [0] = 4; missilerack [1] = 4; missilerack [2] = 4; missilerack [3] = 4;
983     flares = 25;
984     chaffs = 25;
985     bomber = 1;
986     statfirepower = 5;
987     ammo = 2000;
988     dualshot = true;
989   }
990   else if (id == FIGHTER_REDARROW)
991   {
992     maxthrust = 0.33;
993     nimbility = 0.95;
994     manoeverability = 0.52;
995     maxshield = 120;
996     zoom = 0.4;
997     maxtheta = 90.0;
998     maxgamma = 70.0;
999     missilerackn [0] = 2; missilerackn [1] = 3; missilerackn [2] = 3; missilerackn [3] = 2;
1000     missilerack [0] = 7; missilerack [1] = 1; missilerack [2] = 1; missilerack [3] = 7;
1001     flares = 25;
1002     chaffs = 25;
1003     statfirepower = 2;
1004     ammo = 1400;
1005     dualshot = true;
1006   }
1007   else if (id == FIGHTER_BLACKBIRD)
1008   {
1009     maxthrust = 0.3;
1010     nimbility = 1.0;
1011     manoeverability = 0.54;
1012     maxshield = 85;
1013     zoom = 0.33;
1014     maxtheta = 90.0;
1015     maxgamma = 70.0;
1016     missilerackn [0] = 2; missilerackn [1] = 3; missilerackn [2] = 3; missilerackn [3] = 2;
1017     missilerack [0] = 7; missilerack [1] = 1; missilerack [2] = 1; missilerack [3] = 7;
1018     flares = 25;
1019     chaffs = 25;
1020     statfirepower = 2;
1021     ammo = 1400;
1022     dualshot = true;
1023   }
1024   if (id >= FIGHTER1 && id <= FIGHTER2)
1025   {
1026     recthrust = maxthrust / 2.0;
1027     shield = maxshield;
1028     thrust = recthrust = maxthrust / 2;
1029     smoke->type = 1;
1030     impact = 2;
1031     forcez = recthrust;
1032     o->cubex = zoom * cubefac; o->cubey = zoom * cubefac; o->cubez = zoom * cubefac;
1033   }
1034 
1035   if (id == FLAK_AIR1)
1036   {
1037     maxthrust = 0;
1038     thrust = 0;
1039     maxgamma = 0;
1040     maxtheta = 0.03;
1041     manoeverability = 12.0;
1042     shield = maxshield = 80;
1043     zoom = 0.35;
1044   }
1045   if (id == FLARAK_AIR1)
1046   {
1047     maxthrust = 0;
1048     thrust = 0;
1049     maxgamma = 0;
1050     maxtheta = 0.03;
1051     manoeverability = 6.0;
1052     shield = maxshield = 70;
1053     zoom = 0.3;
1054     missiles [6] = 100;
1055   }
1056   if (id >= FLAK1 && id <= FLAK2)
1057   {
1058     o->cubex = zoom * cubefac1; o->cubey = zoom * cubefac1; o->cubez = zoom * cubefac1;
1059   }
1060 
1061   if (id == TANK_AIR1)
1062   {
1063     maxthrust = 0.04;
1064     thrust = 0;
1065     gamma = 180; theta = 0; phi = 0;
1066     maxgamma = 0;
1067     maxtheta = 0.03;
1068     manoeverability = 8.0;
1069     shield = maxshield = 160;
1070     zoom = 0.35;
1071     o->cubex = zoom * 0.7; o->cubey = zoom * 0.45; o->cubez = zoom * 0.7;
1072   }
1073   else if (id == TANK_GROUND1)
1074   {
1075     maxthrust = 0.04;
1076     thrust = 0;
1077     gamma = 180; theta = 0; phi = 0;
1078     maxgamma = 0;
1079     maxtheta = 0.03;
1080     manoeverability = 8.0;
1081     shield = maxshield = 200;
1082     zoom = 0.4;
1083     o->cubex = zoom * 0.7; o->cubey = zoom * 0.5; o->cubez = zoom * 0.7;
1084   }
1085   else if (id == TANK_PICKUP1)
1086   {
1087     maxthrust = 0;
1088     thrust = 0.02;
1089     maxgamma = 0;
1090     maxtheta = 0.03;
1091     manoeverability = 0;
1092     shield = maxshield = 30;
1093     zoom = 0.25;
1094     o->cubex = zoom * 0.7; o->cubey = zoom * 0.55; o->cubez = zoom * 0.7;
1095   }
1096   else if (id == TANK_TRUCK1)
1097   {
1098     maxthrust = 0;
1099     thrust = 0.02;
1100     maxgamma = 0;
1101     maxtheta = 0.03;
1102     manoeverability = 0;
1103     shield = maxshield = 20;
1104     zoom = 0.45;
1105     o->cubex = zoom * 0.6; o->cubey = zoom * 0.35; o->cubez = zoom * 0.6;
1106   }
1107   else if (id == TANK_TRUCK2)
1108   {
1109     maxthrust = 0;
1110     thrust = 0.02;
1111     maxgamma = 0;
1112     maxtheta = 0.03;
1113     manoeverability = 0;
1114     shield = maxshield = 40;
1115     zoom = 0.4;
1116     o->cubex = zoom * 0.6; o->cubey = zoom * 0.35; o->cubez = zoom * 0.6;
1117   }
1118   else if (id == TANK_TRSAM1)
1119   {
1120     maxthrust = 0;
1121     thrust = 0.02;
1122     maxgamma = 0;
1123     maxtheta = 0.03;
1124     manoeverability = 0;
1125     shield = maxshield = 50;
1126     zoom = 0.35;
1127     missiles [6] = 200;
1128     o->cubex = zoom * 0.7; o->cubey = zoom * 0.6; o->cubez = zoom * 0.7;
1129   }
1130   if (id >= TANK1 && id <= TANK2)
1131   {
1132   }
1133 
1134   if (id == SHIP_CRUISER)
1135   {
1136     zoom = 5.0;
1137     maxthrust = 0.05;
1138     thrust = 0.05;
1139     maxgamma = 0;
1140     maxtheta = 0.03;
1141     manoeverability = 4.0;
1142     impact = 20;
1143     shield = maxshield = 5500;
1144     missiles [6] = 200;
1145     o->cubex = zoom * 0.35; o->cubey = zoom * 0.1; o->cubez = zoom * 0.35;
1146   }
1147   else if (id == SHIP_DESTROYER1)
1148   {
1149     zoom = 2.5;
1150     maxthrust = 0.05;
1151     thrust = 0.05;
1152     maxgamma = 0;
1153     maxtheta = 0.03;
1154     manoeverability = 6.0;
1155     impact = 20;
1156     shield = maxshield = 2800;
1157     o->cubex = zoom * 0.4; o->cubey = zoom * 0.12; o->cubez = zoom * 0.4;
1158   }
1159 
1160   float missilethrustbase = 1.2F;
1161   if (id == MISSILE_AIR1)
1162   {
1163     intelligence = 100;
1164     maxthrust = 0.7 * missilethrustbase;
1165     nimbility = 2.5; // old 2.2
1166     manoeverability = 1.5;
1167     ttl = 300 * timestep;
1168     impact = 35;
1169   }
1170   else if (id == MISSILE_AIR2)
1171   {
1172     intelligence = 50;
1173     maxthrust = 0.75 * missilethrustbase;
1174     nimbility = 3.5; // old 3.5
1175     manoeverability = 2.0;
1176     ttl = 320 * timestep;
1177     impact = 45;
1178   }
1179   else if (id == MISSILE_AIR3)
1180   {
1181     intelligence = 0;
1182     maxthrust = 0.8 * missilethrustbase;
1183     nimbility = 4.5;
1184     manoeverability = 2.5;
1185     ttl = 340 * timestep;
1186     impact = 55;
1187   }
1188   else if (id == MISSILE_GROUND1)
1189   {
1190     intelligence = 50;
1191     maxthrust = 0.75 * missilethrustbase;
1192     nimbility = 1.2;
1193     manoeverability = 1.0;
1194     ai = true;
1195     ttl = 300 * timestep;
1196     impact = 400;
1197   }
1198   else if (id == MISSILE_GROUND2)
1199   {
1200     intelligence = 0;
1201     maxthrust = 0.8 * missilethrustbase;
1202     nimbility = 1.5;
1203     manoeverability = 1.0;
1204     ai = true;
1205     ttl = 400 * timestep;
1206     impact = 500;
1207   }
1208   else if (id == MISSILE_DF1)
1209   {
1210     intelligence = 0;
1211     maxthrust = 0.75 * missilethrustbase;
1212     nimbility = 0.0;
1213     manoeverability = 0.0;
1214     ai = true;
1215     ttl = 350 * timestep;
1216     impact = 920;
1217   }
1218   else if (id == MISSILE_FF1)
1219   {
1220     intelligence = 0;
1221     maxthrust = 0.8 * missilethrustbase;
1222     nimbility = 2.0;
1223     manoeverability = 1.3;
1224     ttl = 300 * timestep;
1225     impact = 40;
1226   }
1227   else if (id == MISSILE_FF2)
1228   {
1229     intelligence = 0;
1230     maxthrust = 0.85 * missilethrustbase;
1231     nimbility = 3.0;
1232     manoeverability = 2.0;
1233     ttl = 320 * timestep;
1234     impact = 50;
1235   }
1236   else if (id == MISSILE_MINE1)
1237   {
1238     intelligence = 0;
1239     maxthrust = 0.1;
1240     if (difficulty == 1) maxthrust = 0.14;
1241     else if (difficulty == 2) maxthrust = 0.18;
1242     nimbility = 1.5;
1243     manoeverability = 1.0;
1244     ai = true;
1245     ttl = -1;
1246     impact = 500;
1247     zoom = 0.3;
1248   }
1249   if (id >= MISSILE1 && id <= MISSILE2)
1250   {
1251     o->cubex = zoom; o->cubey = zoom; o->cubez = zoom;
1252   }
1253 
1254   if (id >= STATIC_PASSIVE)
1255   {
1256     intelligence = 0;
1257     maxthrust = 0;
1258     nimbility = 0;
1259     manoeverability = 0;
1260     impact = 5;
1261     maxtheta = 0;
1262     maxgamma = 0;
1263   }
1264   if (id == STATIC_TENT1)
1265   {
1266     shield = maxshield = 80;
1267     zoom = 0.5;
1268     o->cubex = zoom * 0.9; o->cubey = zoom; o->cubez = zoom * 0.9;
1269   }
1270   if (id == STATIC_TENT4)
1271   {
1272     shield = maxshield = 160;
1273     zoom = 1.2;
1274     o->cubex = zoom * 0.7; o->cubey = zoom * 0.42; o->cubez = zoom * 0.7;
1275   }
1276   if (id == STATIC_CONTAINER1)
1277   {
1278     shield = maxshield = 30;
1279     zoom = 1.0;
1280     impact = 20;
1281     o->cubex = zoom * 0.4; o->cubey = zoom * 0.35; o->cubez = zoom * 0.9;
1282   }
1283   if (id == STATIC_HALL1)
1284   {
1285     shield = maxshield = 450;
1286     zoom = 1.8;
1287     impact = 20;
1288     o->cubex = zoom * 0.45; o->cubey = zoom * 0.42; o->cubez = zoom;
1289   }
1290   if (id == STATIC_HALL2)
1291   {
1292     shield = maxshield = 900;
1293     zoom = 2.5;
1294     impact = 20;
1295     o->cubex = zoom; o->cubey = zoom * 0.45; o->cubez = zoom;
1296   }
1297   if (id == STATIC_OILRIG1)
1298   {
1299     shield = maxshield = 1400;
1300     zoom = 3.5;
1301     impact = 20;
1302     o->cubex = zoom * 0.95; o->cubey = zoom * 0.5; o->cubez = zoom * 0.95;
1303   }
1304   if (id == STATIC_COMPLEX1)
1305   {
1306     shield = maxshield = 5000;
1307     zoom = 2.0;
1308     impact = 20;
1309     o->cubex = zoom * 0.75; o->cubey = zoom * 0.6; o->cubez = zoom * 0.75;
1310   }
1311   if (id == STATIC_RADAR1)
1312   {
1313     shield = maxshield = 500;
1314     zoom = 1.3;
1315     impact = 20;
1316     o->cubex = zoom * 0.5; o->cubey = zoom * 0.7; o->cubez = zoom * 0.5;
1317   }
1318   if (id == ASTEROID)
1319   {
1320     shield = maxshield = 100000;
1321     zoom = 0.01 * myrandom (60) + 1.0;
1322     impact = 5;
1323     thrust = 0.25;
1324     maxthrust = 0.25;
1325     forcez = 0.12;
1326     ai = false;
1327     o->cubex = zoom * 0.7; o->cubey = zoom * 0.7; o->cubez = zoom * 0.7;
1328   }
1329   if (id == STATIC_BASE1)
1330   {
1331     shield = maxshield = 5500;
1332     zoom = 4.0;
1333     impact = 20;
1334     o->cubex = zoom * 0.7; o->cubey = zoom * 0.5; o->cubez = zoom * 0.7;
1335   }
1336   if (id == STATIC_DEPOT1)
1337   {
1338     shield = maxshield = 3000;
1339     zoom = 1.5;
1340     impact = 20;
1341     o->cubex = zoom; o->cubey = zoom * 0.5; o->cubez = zoom;
1342   }
1343   if (id == STATIC_BARRIER1)
1344   {
1345     shield = maxshield = 1000;
1346     zoom = 12.0;
1347     impact = 2000;
1348     o->cubex = 0.8; o->cubey = 11; o->cubez = 11;
1349   }
1350   if (id >= STATIC_PASSIVE)
1351   {
1352   }
1353 
1354   if (difficulty == 0) // easy
1355   {
1356     intelligence = 400 - (400 - intelligence) * 1 / 3;
1357     precision = 400 - (400 - precision) * 1 / 3;
1358     aggressivity = 400 - (400 - aggressivity) * 1 / 3;
1359     if (party != 1 && shield > 10) // not player party
1360     {
1361       shield = shield * 8 / 10;
1362       maxshield = shield;
1363     }
1364   }
1365   else if (difficulty == 1) // normal
1366   {
1367     intelligence = 400 - (400 - intelligence) * 2 / 3;
1368     precision = 400 - (400 - precision) * 2 / 3;
1369     aggressivity = 400 - (400 - aggressivity) * 2 / 3;
1370   }
1371   else if (difficulty == 2) // hard
1372   {
1373   }
1374 
1375   this->intelligence = intelligence;
1376   this->precision = precision;
1377   this->aggressivity = aggressivity;
1378   missileCount ();
1379 }
1380 
newinit(int id,int party,int intelligence)1381 void AIObj::newinit (int id, int party, int intelligence)
1382 {
1383   newinit (id, party, intelligence, intelligence, intelligence);
1384 }
1385 
AIObj()1386 AIObj::AIObj ()
1387 {
1388   o = NULL;
1389   zoom = 1.0;
1390   aiinit ();
1391   smoke = new CSmoke (0);
1392 }
1393 
AIObj(Space * space2,CModel * o2,float zoom2)1394 AIObj::AIObj (Space *space2, CModel *o2, float zoom2)
1395 {
1396   this->space = space2;
1397   o = o2;
1398   zoom = zoom2;
1399   aiinit ();
1400   smoke = new CSmoke (0);
1401   space->addObject (this);
1402 }
1403 
~AIObj()1404 AIObj::~AIObj ()
1405 {
1406   delete smoke;
1407 }
1408 
initValues(DynamicObj * dobj,float phi)1409 void AIObj::initValues (DynamicObj *dobj, float phi)
1410 {
1411   float fac = zoom / 8;
1412   if (dobj->id == FLARE1 || dobj->id == CHAFF1) fac = -fac;
1413   // use the exact polar coordinates because of gamma and theta
1414   float cgamma = gamma;
1415   dobj->tl->x = tl->x + COS(cgamma) * SIN(phi) * fac;
1416   dobj->tl->y = tl->y - SIN(cgamma) * fac;
1417   if ((id >= FLAK1 && id <= FLAK2) || (id >= TANK1 && id <= TANK2))
1418     dobj->tl->y += fac;
1419   dobj->tl->z = tl->z + COS(cgamma) * COS(phi) * fac;
1420   dobj->phi = phi;
1421   dobj->rectheta = dobj->theta;
1422   dobj->forcex = forcex;
1423   dobj->forcey = forcey;
1424   dobj->forcez = forcez;
1425   dobj->rot->setAngles ((short) (90 + dobj->gamma - 180), (short) dobj->theta + 180, (short) -dobj->phi);
1426 }
1427 
fireCannon(DynamicObj * laser,float phi)1428 void AIObj::fireCannon (DynamicObj *laser, float phi)
1429 {
1430   if (firecannonttl > 0) return;
1431   if (ammo == 0) return;
1432   ammo --;
1433   laser->thrust = 0;
1434   laser->recthrust = laser->thrust;
1435   laser->manoeverability = 0.0;
1436   laser->maxthrust = 0;
1437   if (target != NULL && ai)
1438   {
1439     if (target->active)
1440     {
1441       // exact calculation to hit enemy (non-static turret!)
1442       if (id >= FIGHTER1 && id <= FIGHTER2)
1443         laser->gamma = gamma;
1444       else
1445         laser->gamma = 180.0 + atan ((target->tl->y - tl->y) / distance (target)) * 180.0 / pitab;
1446     }
1447   }
1448   else
1449     laser->gamma = gamma; // + 90.0;
1450   laser->party = party;
1451   laser->ttl = 80 * timestep;
1452   laser->shield = 1;
1453   laser->immunity = (int) (zoom * 12) * timestep;
1454   laser->source = this;
1455   laser->phi = phi;
1456   laser->theta = theta;
1457   initValues (laser, phi);
1458   float fac = 0.7F;
1459   laser->forcex += COS(laser->gamma) * SIN(laser->phi) * fac;
1460   laser->forcey -= SIN(laser->gamma) * fac;
1461   laser->forcez += COS(laser->gamma) * COS(laser->phi) * fac;
1462   laser->activate ();
1463   firecannonttl += 45;
1464   if (day)
1465   {
1466     if (dualshot)
1467       laser->o = &model_cannon1b;
1468     else
1469       laser->o = &model_cannon1;
1470   }
1471   else
1472   {
1473     if (dualshot)
1474       laser->o = &model_cannon2b;
1475     else
1476       laser->o = &model_cannon2;
1477   }
1478 }
1479 
fireCannon(DynamicObj ** laser,float phi)1480 void AIObj::fireCannon (DynamicObj **laser, float phi)
1481 {
1482   int i;
1483   if (firecannonttl > 0) return;
1484   if (ammo == 0) return;
1485   for (i = 0; i < maxlaser; i ++)
1486   {
1487     if (!laser [i]->active) break;
1488   }
1489   if (i < maxlaser)
1490   {
1491     fireCannon (laser [i], phi);
1492   }
1493 }
1494 
fireCannon(DynamicObj ** laser)1495 void AIObj::fireCannon (DynamicObj **laser)
1496 {
1497   if (firecannonttl > 0) return;
1498   if (ammo == 0) return;
1499   fireCannon (laser, phi);
1500 }
1501 
fireMissile2(int id,AIObj * missile,AIObj * target)1502 void AIObj::fireMissile2 (int id, AIObj *missile, AIObj *target)
1503 {
1504   char buf [STDSIZE];
1505   if (debuglevel == LOG_ALL)
1506   {
1507     sprintf (buf, "Missile: party=%d, id=%d", party, id);
1508     display (buf, LOG_ALL);
1509   }
1510   ttf = 50 * timestep;
1511   missile->dinit ();
1512   missile->aiinit ();
1513   missile->newinit (id, party, 0);
1514   initValues (missile, phi);
1515   missile->id = id;
1516   missile->explode = 0;
1517   missile->thrust = thrust + 0.001;
1518   missile->recthrust = missile->maxthrust;
1519   missile->gamma = gamma;
1520   missile->target = target;
1521   missile->recgamma = gamma;
1522   missile->shield = 1;
1523   missile->party = party;
1524   missile->immunity = (45 + (int) (zoom * 6.0)) * timestep;
1525   missile->dtheta = 0;
1526   missile->dgamma = 0;
1527   missile->source = this;
1528   missile->activate ();
1529   if (id >= FIGHTER1 && id <= FIGHTER2)
1530   {
1531     missile->manoeverheight = 30 * timestep;
1532     missile->recheight = missile->tl->y - l->getHeight (missile->tl->x, missile->tl->z) - 4;
1533   }
1534 }
1535 
fireFlare2(DynamicObj * flare)1536 void AIObj::fireFlare2 (DynamicObj *flare)
1537 {
1538   char buf [STDSIZE];
1539   if (debuglevel == LOG_ALL)
1540   {
1541     sprintf (buf, "Flare: party=%d", party);
1542     display (buf, debuglevel);
1543   }
1544   flare->dinit ();
1545   flare->thrust = 0;
1546   flare->realspeed = 0;
1547   flare->recthrust = 0;
1548   flare->manoeverability = 0.0;
1549   flare->maxthrust = 1.0;
1550   flare->gamma = 0;
1551   flare->party = party;
1552   flare->ttl = 80 * timestep;
1553   flare->shield = 1;
1554   flare->immunity = (int) (zoom * 12) * timestep;
1555   flare->source = this;
1556   flare->phi = phi;
1557   flare->id = FLARE1;
1558   initValues (flare, phi);
1559   flare->activate ();
1560   flare->explode = 0;
1561 }
1562 
fireChaff2(DynamicObj * chaff)1563 void AIObj::fireChaff2 (DynamicObj *chaff)
1564 {
1565   char buf [STDSIZE];
1566   if (debug == LOG_ALL)
1567   {
1568     sprintf (buf, "Chaff: party=%d", party);
1569     display (buf, LOG_ALL);
1570   }
1571   chaff->dinit ();
1572   chaff->thrust = 0;
1573   chaff->realspeed = 0;
1574   chaff->recthrust = 0;
1575   chaff->manoeverability = 0.0;
1576   chaff->maxthrust = 1.0;
1577   chaff->gamma = 0;
1578   chaff->party = party;
1579   chaff->ttl = 80 * timestep;
1580   chaff->shield = 1;
1581   chaff->immunity = (int) (zoom * 12) * timestep;
1582   chaff->source = this;
1583   chaff->phi = phi;
1584   chaff->id = CHAFF1;
1585   initValues (chaff, phi);
1586   chaff->activate ();
1587   chaff->explode = 0;
1588   chaff->zoom = 0.12F;
1589 }
1590 
firstMissile()1591 int AIObj::firstMissile ()
1592 {
1593   int i = 0;
1594   while (!missiles [i])
1595   {
1596     i ++;
1597     if (i >= missiletypes) return 0;
1598   }
1599   ttf = 50 * timestep;
1600   return i;
1601 }
1602 
nextMissile(int from)1603 int AIObj::nextMissile (int from)
1604 {
1605   int i = from + 1;
1606   if (i >= missiletypes) i = 0;
1607   while (!missiles [i])
1608   {
1609     i ++;
1610     if (i >= missiletypes) i = 0;
1611     if (i == from) break;
1612   }
1613   ttf = 50 * timestep;
1614   return i;
1615 }
1616 
haveMissile(int id)1617 bool AIObj::haveMissile (int id)
1618 {
1619   char buf [STDSIZE];
1620   id -= MISSILE1;
1621   if (id < 0 || id >= missiletypes)
1622   {
1623     sprintf (buf, "Wrong missile ID in %s, line %d", __FILE__, __LINE__);
1624     display (buf, LOG_ERROR);
1625   }
1626   if (missiles [id] > 0)
1627     return true;
1628   return false;
1629 }
1630 
haveMissile()1631 bool AIObj::haveMissile () // due to missiletype
1632 {
1633   if (missiles [missiletype] > 0)
1634     return true;
1635   return false;
1636 }
1637 
decreaseMissile(int id)1638 void AIObj::decreaseMissile (int id)
1639 {
1640   char buf [STDSIZE];
1641   int i;
1642   id -= MISSILE1;
1643   if (id < 0 || id >= missiletypes)
1644   {
1645     sprintf (buf, "Wrong missile ID in %s, line %d", __FILE__, __LINE__);
1646     display (buf, LOG_ERROR);
1647   }
1648   missiles [id] --;
1649   int ptrrack = 0, maxrack = 0;
1650   for (i = 0; i < missileracks; i ++)
1651     if (missilerack [i] == id)
1652       if (missilerackn [i] > maxrack)
1653       {
1654         ptrrack = i;
1655         maxrack = missilerackn [i];
1656       }
1657   if (maxrack > 0)
1658   {
1659     missilerackn [ptrrack] --;
1660     refscale [ptrrack * 3 + 2 - missilerackn [ptrrack]] = 0;
1661   }
1662 }
1663 
fireMissile(int id,AIObj ** missile,AIObj * target)1664 bool AIObj::fireMissile (int id, AIObj **missile, AIObj *target)
1665 {
1666   int i;
1667   if (!haveMissile (id)) return false;
1668   if (ttf > 0) return false;
1669   for (i = 0; i < maxmissile; i ++)
1670   {
1671     if (missile [i]->ttl <= 0) break;
1672   }
1673   if (i < maxmissile)
1674   {
1675     fireMissile2 (id, missile [i], target);
1676     decreaseMissile (id);
1677     firemissilettl = 20 * timestep;
1678     return true;
1679   }
1680   return false;
1681 }
1682 
fireMissile(AIObj ** missile,AIObj * target)1683 bool AIObj::fireMissile (AIObj **missile, AIObj *target)
1684 {
1685   if (ttf > 0) return false;
1686   return fireMissile (missiletype + MISSILE1, missile, (AIObj *) target);
1687 }
1688 
fireMissile(int id,AIObj ** missile)1689 bool AIObj::fireMissile (int id, AIObj **missile)
1690 {
1691   if (ttf > 0) return false;
1692   return fireMissile (id, missile, (AIObj *) target);
1693 }
1694 
fireMissile(AIObj ** missile)1695 bool AIObj::fireMissile (AIObj **missile)
1696 {
1697   if (ttf > 0) return false;
1698   return fireMissile (missiletype + MISSILE1, missile);
1699 }
1700 
fireFlare(DynamicObj ** flare,AIObj ** missile)1701 bool AIObj::fireFlare (DynamicObj **flare, AIObj **missile)
1702 {
1703   int i, i2;
1704   if (flares <= 0) return false;
1705   if (fireflarettl > 0) return false;
1706   for (i = 0; i < maxflare; i ++)
1707   {
1708     if (flare [i]->ttl <= 0) break;
1709   }
1710   if (i < maxflare)
1711   {
1712     fireFlare2 (flare [i]);
1713     flares --;
1714     fireflarettl = 8 * timestep;
1715     for (i2 = 0; i2 < maxmissile; i2 ++)
1716     {
1717       if (missile [i2]->ttl > 0)
1718       {
1719         if (missile [i2]->id >= MISSILE_AIR1 && missile [i2]->id <= MISSILE_AIR3) // only heat seeking missiles
1720           if (missile [i2]->target == this) // only change target if angle is good
1721           {
1722             bool hit = false;
1723             if (easymodel == 1)
1724             {
1725               if (myrandom ((int) (theta + 20)) > 50) hit = true;
1726             }
1727             else if (easymodel == 2)
1728             {
1729               if (myrandom ((int) (fabs (elevatoreffect) * 90 + 20)) > 50) hit = true;
1730             }
1731             if (hit)
1732             {
1733               if (debuglevel == LOG_ALL)
1734               {
1735                 display ("Missile to flare", LOG_ALL);
1736               }
1737               missile [i2]->target = flare [i];
1738             }
1739           }
1740       }
1741     }
1742     return true;
1743   }
1744   return false;
1745 }
1746 
fireChaff(DynamicObj ** chaff,AIObj ** missile)1747 bool AIObj::fireChaff (DynamicObj **chaff, AIObj **missile)
1748 {
1749   int i, i2;
1750   if (chaffs <= 0) return false;
1751   if (firechaffttl > 0) return false;
1752   for (i = 0; i < maxchaff; i ++)
1753   {
1754     if (chaff [i]->ttl <= 0) break;
1755   }
1756   if (i < maxchaff)
1757   {
1758     fireChaff2 (chaff [i]);
1759     chaffs --;
1760     firechaffttl = 8 * timestep;
1761     for (i2 = 0; i2 < maxmissile; i2 ++)
1762     {
1763       if (missile [i2]->ttl > 0)
1764       {
1765         if (missile [i2]->id > MISSILE_AIR3) // only radar seeking missiles
1766           if (missile [i2]->target == this) // only change target if angle is good
1767           {
1768             bool hit = false;
1769             if (easymodel == 1)
1770             {
1771               if (myrandom ((int) (theta + 20)) > 50) hit = true;
1772             }
1773             else if (easymodel == 2)
1774             {
1775               if (myrandom ((int) (fabs (elevatoreffect) * 90 + 20)) > 50) hit = true;
1776             }
1777             if (hit)
1778             {
1779               if (debuglevel == LOG_ALL)
1780               {
1781                 display ("Missile to chaff", LOG_ALL);
1782               }
1783               missile [i2]->target = chaff [i];
1784             }
1785           }
1786       }
1787     }
1788     return true;
1789   }
1790   return false;
1791 }
1792 
fireMissileAir(AIObj ** missile,AIObj * target)1793 bool AIObj::fireMissileAir (AIObj **missile, AIObj *target)
1794 {
1795   if (ttf > 0) return false;
1796   if (target->id >= MOVING_GROUND) return false;
1797   if (haveMissile (MISSILE_AIR3))
1798     return fireMissile (MISSILE_AIR3, missile, (AIObj *) target);
1799   else if (haveMissile (MISSILE_AIR2))
1800     return fireMissile (MISSILE_AIR2, missile, (AIObj *) target);
1801   else if (haveMissile (MISSILE_AIR1))
1802     return fireMissile (MISSILE_AIR1, missile, (AIObj *) target);
1803   return false;
1804 }
1805 
selectMissileAir(AIObj ** missile)1806 bool AIObj::selectMissileAir (AIObj **missile)
1807 {
1808   bool sel = false;
1809   if (haveMissile (MISSILE_AIR3)) { missiletype = MISSILE_AIR3 - MISSILE1; sel = true; }
1810   else if (haveMissile (MISSILE_AIR2)) { missiletype = MISSILE_AIR2 - MISSILE1; sel = true; }
1811   else if (haveMissile (MISSILE_AIR1)) { missiletype = MISSILE_AIR1 - MISSILE1; sel = true; }
1812   return sel;
1813 }
1814 
fireMissileAirFF(AIObj ** missile,AIObj * target)1815 bool AIObj::fireMissileAirFF (AIObj **missile, AIObj *target)
1816 {
1817   if (ttf > 0) return false;
1818   if (target->id >= MOVING_GROUND) return false;
1819   if (haveMissile (MISSILE_FF2))
1820     return fireMissile (MISSILE_FF2, missile, (AIObj *) target);
1821   else if (haveMissile (MISSILE_FF1))
1822     return fireMissile (MISSILE_FF1, missile, (AIObj *) target);
1823   return false;
1824 }
1825 
selectMissileAirFF(AIObj ** missile)1826 bool AIObj::selectMissileAirFF (AIObj **missile)
1827 {
1828   bool sel = false;
1829   if (haveMissile (MISSILE_FF2)) { missiletype = MISSILE_FF2 - MISSILE1; sel = true; }
1830   else if (haveMissile (MISSILE_FF1)) { missiletype = MISSILE_FF1 - MISSILE1; sel = true; }
1831   return sel;
1832 }
1833 
fireMissileGround(AIObj ** missile)1834 bool AIObj::fireMissileGround (AIObj **missile)
1835 {
1836   if (ttf > 0) return false;
1837   if (target->id < MOVING_GROUND) return false;
1838   if (haveMissile (MISSILE_GROUND2))
1839     return fireMissile (MISSILE_GROUND2, missile, (AIObj *) target);
1840   else if (haveMissile (MISSILE_GROUND1))
1841     return fireMissile (MISSILE_GROUND1, missile, (AIObj *) target);
1842   return false;
1843 }
1844 
selectMissileGround(AIObj ** missile)1845 bool AIObj::selectMissileGround (AIObj **missile)
1846 {
1847   bool sel = false;
1848   if (haveMissile (MISSILE_GROUND2)) { missiletype = MISSILE_GROUND2 - MISSILE1; sel = true; }
1849   else if (haveMissile (MISSILE_GROUND1)) { missiletype = MISSILE_GROUND1 - MISSILE1; sel = true; }
1850   return sel;
1851 }
1852 
targetNearestGroundEnemy(AIObj ** f)1853 void AIObj::targetNearestGroundEnemy (AIObj **f)
1854 {
1855   int i;
1856   float d = 1E12; //10000 is too low
1857   ttf = 50 * timestep;
1858   for (i = 0; i < maxfighter; i ++)
1859   {
1860     if (this != f [i] && party != f [i]->party && f [i]->active)
1861     {
1862       float phi = getAngle (f [i]);
1863       float d2 = distance (f [i]) * (60 + fabs (phi)); // prefer enemies in front
1864       if (bomber)
1865         if (f [i]->id < MOVING_GROUND)
1866           d2 += 1E10; // only use this target if no ground targets exist
1867       if (d2 < d)
1868       {
1869         d = d2;
1870         target = f [i];
1871       }
1872     }
1873   }
1874   if (target)
1875     if (distance (target) > 400)
1876     { target = NULL; }
1877 }
1878 
targetNearestEnemy(AIObj ** f)1879 void AIObj::targetNearestEnemy (AIObj **f)
1880 {
1881   int i;
1882   float d = 1E12; //10000 is too low
1883   ttf = 50 * timestep;
1884   for (i = 0; i < maxfighter; i ++)
1885   {
1886     if (this != f [i] && party != f [i]->party && f [i]->active)
1887     {
1888       float phi = getAngle (f [i]);
1889       float d2 = distance (f [i]) * (60 + fabs (phi)); // prefer enemies in front
1890       if (d2 < d)
1891       {
1892         d = d2;
1893         target = f [i];
1894       }
1895     }
1896   }
1897   if (!ai && target)
1898     if (distance (target) > 400)
1899       target = NULL;
1900 }
1901 
targetLockingEnemy(AIObj ** f)1902 void AIObj::targetLockingEnemy (AIObj **f)
1903 {
1904   int i;
1905   ttf = 50 * timestep;
1906   if (target == NULL) target = f [0];
1907   for (i = 0; i < maxfighter; i ++)
1908     if (target == f [i])
1909       break;
1910   int z = 0;
1911   do
1912   {
1913     i ++;
1914     if (i >= maxfighter) { i = 0; z ++; }
1915   } while ((!f [i]->active || f [i]->party == party || f [i]->target != this || distance (f [i]) > 200) && z <= 1);
1916   target = f [i];
1917   if (z > 1 && !ai) target = NULL;
1918 }
1919 
targetNext(AIObj ** f)1920 void AIObj::targetNext (AIObj **f)
1921 {
1922   int i;
1923   ttf = 50 * timestep;
1924   if (target == NULL) target = f [0];
1925   for (i = 0; i < maxfighter; i ++)
1926     if (target == f [i])
1927       break;
1928   int z = 0;
1929   do
1930   {
1931     i ++;
1932     if (i >= maxfighter) i = 0;
1933     if (f [i] == this)
1934     { i ++; z ++; }
1935     if (i >= maxfighter) i = 0;
1936   } while ((!f [i]->active || distance (f [i]) > 400) && z <= 1);
1937   target = f [i];
1938   if (z > 1 && !ai) target = NULL;
1939 }
1940 
targetNextEnemy(AIObj ** f)1941 void AIObj::targetNextEnemy (AIObj **f)
1942 {
1943   int i;
1944   ttf = 50 * timestep;
1945   if (target == NULL) target = f [0];
1946   for (i = 0; i < maxfighter; i ++)
1947     if (target == f [i])
1948       break;
1949   int z = 0;
1950   do
1951   {
1952     i ++;
1953     if (i >= maxfighter) i = 0;
1954     if (f [i] == this)
1955     { i ++; z ++; }
1956     if (i >= maxfighter) i = 0;
1957   } while ((!f [i]->active || distance (f [i]) > 400 || party == f [i]->party) && z <= 1);
1958   target = f [i];
1959   if (z > 1 && !ai) target = NULL;
1960 }
1961 
targetPrevious(AIObj ** f)1962 void AIObj::targetPrevious (AIObj **f)
1963 {
1964   int i;
1965   ttf = 50 * timestep;
1966   if (target == NULL) target = f [0];
1967   for (i = 0; i < maxfighter; i ++)
1968     if (target == f [i])
1969       break;
1970   int z = 0;
1971   do
1972   {
1973     i --;
1974     if (i < 0) i = maxfighter - 1;
1975     if (f [i] == this)
1976     { i --; z ++; }
1977     if (i < 0) i = maxfighter - 1;
1978   } while ((!f [i]->active || distance (f [i]) > 400) && z <= 1);
1979   target = f [i];
1980   if (z > 1 && !ai) target = NULL;
1981 }
1982 
1983 // core AI method
aiAction(Uint32 dt,AIObj ** f,AIObj ** m,DynamicObj ** c,DynamicObj ** flare,DynamicObj ** chaff)1984 void AIObj::aiAction (Uint32 dt, AIObj **f, AIObj **m, DynamicObj **c, DynamicObj **flare, DynamicObj **chaff)
1985 {
1986   int i;
1987 
1988   timer += dt;
1989 
1990   if (!active && !draw) // not active, not drawn, then exit
1991   {
1992     return;
1993   }
1994 
1995   if (firecannonttl > 0) firecannonttl -= dt; // time to fire the next missile
1996   if (firemissilettl > 0) firemissilettl -= dt; // time to fire the next missile
1997   if (fireflarettl > 0) fireflarettl -= dt; // time to fire the next flare
1998   if (firechaffttl > 0) firechaffttl -= dt; // time to fire the next chaff
1999   if (smokettl > 0) smokettl -= dt; // time to fire the next chaff
2000 
2001   // move object according to our physics
2002   move (dt);
2003 
2004   float timefac = (float) dt / (float) timestep;
2005 
2006   if (id >= STATIC_PASSIVE) // no AI for static ground objects (buildings)
2007     return;
2008 
2009   // set smoke
2010   if ((id >= MISSILE1 && id < MISSILE_MINE1) || (id >= FIGHTER1 && id <= FIGHTER2)) // missile or fighter
2011   {
2012     float sz = COS(gamma) * COS(phi) * zoom * 1.1; // polar (spherical) coordinates
2013     float sy = -SIN(gamma) * zoom * 1.1;
2014     float sx = COS(gamma) * SIN(phi) * zoom * 1.1;
2015 
2016     // some smoke elements per discrete movement
2017     float fg = sqrt (forcex * forcex + forcey * forcey + forcez * forcez) * 13;
2018     if (fg >= MAXSMOKEELEM) fg = (float) MAXSMOKEELEM - 0.5;
2019     for (i = 0; i < (int) fg; i ++)
2020     {
2021       float fac = (float) i / fg;
2022       smoke->setSmoke (tl->x - sx - forcex * fac, tl->y - sy - forcey * fac, tl->z - sz - forcez * fac, (int) phi, 39 - i);
2023     }
2024 /*    smoke->setSmoke (tl->x - sx - forcex * 0.6, tl->y - sy - forcey * 0.6, tl->z - sz - forcez * 0.6, (int) phi, 36);
2025     smoke->setSmoke (tl->x - sx - forcex * 0.4, tl->y - sy - forcey * 0.4, tl->z - sz - forcez * 0.4, (int) phi, 37);
2026     smoke->setSmoke (tl->x - sx - forcex * 0.2, tl->y - sy - forcey * 0.2, tl->z - sz - forcez * 0.2, (int) phi, 38);
2027     smoke->setSmoke (tl->x - sx, tl->y - sy, tl->z - sz, (int) phi, 39);
2028     smoke->move (dt, 5);*/
2029     smoke->move (dt, (int) fg + 1);
2030     smokettl += timestep;
2031   }
2032 
2033   if (!active) // not active, then exit
2034   {
2035     return;
2036   }
2037   if (explode > 0 || sink > 0) // exploding or sinking, then exit
2038   {
2039     thrust = 0;
2040     return;
2041   }
2042 
2043   // do expensive calculations only once
2044   float myheight = l->getExactHeight (tl->x, tl->z);
2045   float targetheight = tl->y;
2046   if (target != NULL)
2047     targetheight = l->getExactHeight (target->tl->x, target->tl->z);
2048   if (target != NULL)
2049     disttarget = distance (target); // distance to target
2050   else
2051     disttarget = 1;
2052 
2053   // get a new target if necessary
2054   if (id >= MISSILE1 && id <= MISSILE2)
2055   {
2056     if (target == NULL)
2057       ttl = 0;
2058     else if (!target->active)
2059       ttl = 0;
2060   }
2061 
2062   if (target == NULL)
2063   {
2064     if (bomber)
2065       targetNearestGroundEnemy (f);
2066     else
2067       targetNearestEnemy (f);
2068   }
2069   if (target != NULL)
2070     if (!target->active)
2071     {
2072       if (bomber)
2073         targetNearestGroundEnemy (f);
2074       else
2075         targetNearestEnemy (f);
2076     }
2077 
2078   if (id >= FIGHTER1 && id <= FIGHTER2) // for fighters do the following
2079   {
2080     if (haveMissile () && target != NULL)
2081     {
2082       float dgamma = atan ((target->tl->y - tl->y) / disttarget) * 180 / PI - (gamma - 180);
2083       float dphi = getAngle (target);
2084       if (missiletype == MISSILE_DF1 - MISSILE1)
2085       {
2086         ttf = 0;
2087       }
2088       else if (fabs (dphi) < 50 && fabs (dgamma) < 50 && party != target->party)
2089       {
2090         if (disttarget < 75)
2091         {
2092           if (ttf > 0)
2093           {
2094             if (missiletype >= 0 && missiletype <= 2)
2095             {
2096               if (target->id >= FIGHTER1 && target->id <= FIGHTER2)
2097               {
2098                 float dphi = fabs (phi - target->phi);
2099                 if (dphi > 270) dphi = 360 - dphi;
2100                 if (dphi < 45)
2101                   ttf -= 2 * dt;
2102                 else
2103                   ttf = 50 * timestep;
2104               }
2105             }
2106             else if (missiletype == 6 || missiletype == 7)
2107             {
2108               if (target->id >= FIGHTER1 && target->id <= FIGHTER2)
2109               {
2110                 ttf -= 2 * dt;
2111               }
2112             }
2113             else
2114             {
2115               if (target->id > FIGHTER2)
2116               {
2117                 ttf -= 2 * dt;
2118               }
2119             }
2120           }
2121         }
2122       }
2123       else
2124       {
2125         ttf = 50 * timestep;
2126       }
2127     }
2128   }
2129 
2130   if (!ai) return;
2131 
2132 
2133 
2134 /*
2135 // The following model would be the REAL AI behaviour!
2136 // However enemies are too weak!
2137 
2138   CVector3 targetvec;
2139   if (target != NULL)
2140   {
2141     targetvec.take (target->tl);
2142     float disttarget = distance (&targetvec);
2143     if (disttarget > 30) disttarget = 30;
2144     targetvec.x += target->forcex * disttarget / 5;
2145     targetvec.y += target->forcey * disttarget / 5;
2146     targetvec.z += target->forcez * disttarget / 5;
2147   }
2148   int lsdst = 5;
2149   float flyx1 = tl->x + forcex * lsdst, flyz1 = tl->z + forcez * lsdst;
2150   float flyx2 = tl->x + forcex * lsdst * 4, flyz2 = tl->z + forcez * lsdst * 4;
2151   float h1 = tl->y - l->getMaxHeight (flyx1, flyz1);
2152   float h2 = tl->y - l->getMaxHeight (flyx2, flyz2);
2153   if (h1 < 5 || h1 > 25)
2154   {
2155     targetvec.x = flyx1;
2156     targetvec.z = flyz1;
2157     targetvec.y = l->getMaxHeight (flyx1, flyz1) + 15;
2158   }
2159   disttarget = distance (&targetvec);
2160 
2161 
2162 
2163   float dgamma = getAngleH (&targetvec);
2164   float dphi = getAngle (&targetvec);
2165   if (fabs (dphi) < 1E-5) dphi = 1E-5;
2166   float delta = atan (dgamma / dphi) * 180 / PI;
2167   if (dphi > 0) delta -= 180;
2168   rectheta = -delta - 90;
2169   if (rectheta < -180) rectheta += 360;
2170   if (rectheta >= 180) rectheta -= 360;
2171   recelevatoreffect = 1;
2172   if (disttarget > 50 && fabs (dphi) < 25)
2173   {
2174     rectheta = 0;
2175     recelevatoreffect = 0;
2176     if (fabs (theta - rectheta) < 5) recrolleffect = 0;
2177     else if (theta - rectheta >= 5) recrolleffect = -1;
2178     else if (theta - rectheta <= -5) recrolleffect = 1;
2179   }
2180   else if (fabs (theta - rectheta) > 150 && fabs (dphi) < 30 && fabs (dgamma) < 30)
2181   {
2182     recrolleffect = 0;
2183     recelevatoreffect = -0.5;
2184   }
2185   else if (theta - rectheta > 50)
2186   {
2187     recrolleffect = -1;
2188     recelevatoreffect = 0;
2189   }
2190   else if (theta - rectheta < -50)
2191   {
2192     recrolleffect = 1;
2193     recelevatoreffect = 0;
2194   }
2195   else if (theta - rectheta > 20)
2196   {
2197     recrolleffect = -1;
2198     recelevatoreffect = 1;
2199   }
2200   else if (theta - rectheta < -20)
2201   {
2202     recrolleffect = 1;
2203     recelevatoreffect = 1;
2204   }
2205   else
2206   {
2207     recrolleffect = 0;
2208     recelevatoreffect = 1;
2209   }
2210   easymodel = 2;
2211   if (phi >= 360) phi -= 360;
2212   if (phi < 0) phi += 360;
2213   if (gamma >= 360) gamma -= 360;
2214   if (gamma < 0) gamma += 360;
2215   thrust = maxthrust * 0.7;
2216 
2217   float pulljoystick = 0.005;
2218   float nocorrection = 0.1;
2219   if (recrolleffect > rolleffect + nocorrection) rolleffect += pulljoystick * timestep;
2220   else if (recrolleffect < rolleffect - nocorrection) rolleffect -= pulljoystick * timestep;
2221   if (recelevatoreffect > elevatoreffect + nocorrection) elevatoreffect += pulljoystick * timestep;
2222   else if (recelevatoreffect < elevatoreffect - nocorrection) elevatoreffect -= pulljoystick * timestep;
2223 
2224   if (target)
2225   {
2226     if (disttarget < 20 && dphi < 5 && dgamma < 5)
2227       fireCannon (laser);
2228   }
2229 
2230   if (disttarget < 5 && dphi < 90 && dgamma < 90)
2231   {
2232     thrust = maxthrust / 2;
2233   }
2234   else
2235   {
2236     thrust = maxthrust;
2237   }
2238 
2239   return;
2240 */
2241 
2242 
2243 
2244   // which height???
2245   float recheight2; // this is the height, the object wants to achieve
2246   int lsdist = 15;
2247   float flyx = tl->x + forcex * lsdist, flyz = tl->z + forcez * lsdist;
2248   int flyxs = l->getCoord ((int) flyx), flyzs = l->getCoord ((int) flyz);
2249   {
2250     if (manoeverheight > 0)
2251     {
2252       // precalculated height
2253       recheight2 = l->getExactHeight (flyx, flyz) + recheight;
2254     }
2255     else
2256     {
2257        // missiles and non intelligent objects will not change their height due to the surface
2258       if ((id >= MISSILE1 && id <= MISSILE2 && target != NULL) ||
2259           (tl->y - myheight > 8 && target != NULL && tl->y - myheight < 50/* && !manoeverheight*/))
2260       {
2261         recheight2 = target->tl->y - 8 * target->thrust * SIN(target->gamma);
2262       }
2263       else
2264       {
2265         // precalculated height
2266         float flyx2 = tl->x + forcex * lsdist * 3, flyz2 = tl->z + forcez * lsdist * 3;
2267         float flyx3 = tl->x + forcex * lsdist * 8, flyz3 = tl->z + forcez * lsdist * 8;
2268         float h1 = l->getMaxHeight (flyx, flyz);
2269         float h2 = l->getMaxHeight (flyx2, flyz2);
2270         float h3 = l->getMaxHeight (flyx3, flyz3);
2271         h1 = h1 > h2 ? h1 : h2;
2272         h1 = h1 > h3 ? h1 : h3;
2273         recheight2 = recheight + h1;
2274       }
2275     }
2276   }
2277 
2278   // fire flares and chaff
2279   if (id >= FIGHTER1 && id <= FIGHTER2) // for fighters do the following
2280   {
2281     if (manoevertheta <= 0)
2282       for (i = 0; i < maxmissile; i ++)
2283         if (m [i]->ttl > 0)
2284           if (m [i]->target == this)
2285           {
2286             if (m [i]->id >= 0 && m [i]->id <= MISSILE_AIR3)
2287             {
2288               if ((easymodel == 1 && fabs (theta) >= 30) || (easymodel == 2 && fplayer->elevatoreffect >= 0.5))
2289               {
2290                 fireFlare (flare, m);
2291                 fireflarettl += intelligence / 20 * timestep;
2292               }
2293               manoevertheta = 35 * timestep;
2294             }
2295             else
2296             {
2297               if ((easymodel == 1 && fabs (theta) >= 30) || (easymodel == 2 && fplayer->elevatoreffect >= 0.5))
2298               {
2299                 fireChaff (chaff, m);
2300                 firechaffttl += intelligence / 20 * timestep;
2301               }
2302               manoevertheta = 35 * timestep;
2303             }
2304           }
2305   }
2306 
2307   // manoevers (may use the height information)
2308   if (manoeverstate && active && draw)
2309   {
2310     easymodel = 2;
2311 
2312     if (manoeverstate == 1) // Immelmann
2313     {
2314       recelevatoreffect = 0.05;
2315       if (fabs (theta) > 10) recrolleffect = -1;
2316       else
2317       {
2318         recrolleffect = 0;
2319         manoeverstate = 2;
2320       }
2321     }
2322     else if (manoeverstate == 2)
2323     {
2324       if (fabs (theta) <= 150)
2325       {
2326         recrolleffect = 0;
2327         recelevatoreffect = 1;
2328       }
2329       else
2330       {
2331         manoeverstate = 3;
2332       }
2333     }
2334     else if (manoeverstate == 3)
2335     {
2336       if (gamma < 170 || gamma > 190)
2337       {
2338         recrolleffect = 0;
2339         recelevatoreffect = 1;
2340       }
2341       else
2342       {
2343         manoeverstate = 4;
2344       }
2345     }
2346     else if (manoeverstate == 4)
2347     {
2348       if (fabs (theta) > 20)
2349       {
2350         recrolleffect = 1;
2351         recelevatoreffect = 0.05;
2352       }
2353       else
2354       {
2355         manoeverstate = 0;
2356       }
2357     }
2358 
2359     if (manoeverstate == 10) // climb vertical
2360     {
2361       recrolleffect = 0;
2362       recelevatoreffect = 1;
2363       if (gamma > 260 || gamma < 90)
2364       {
2365         recrolleffect = 0;
2366         recelevatoreffect = 0;
2367         manoeverstate = 11;
2368       }
2369     }
2370     else if (manoeverstate == 11)
2371     {
2372       if (fabs (tl->y - myheight) > 3)
2373       {
2374         manoeverstate = 12;
2375       }
2376     }
2377     else if (manoeverstate == 12)
2378     {
2379       recelevatoreffect = -0.5;
2380       if (gamma > 170 && gamma < 190)
2381       {
2382         recelevatoreffect = 0;
2383         manoeverstate = 0;
2384       }
2385     }
2386 
2387     if (manoeverstate == 20) // Roll
2388     {
2389       recelevatoreffect = 0.55;
2390       recrolleffect = 1;
2391       if (theta > 80 && theta < 90)
2392       {
2393         manoeverstate = 21;
2394       }
2395     }
2396     else if (manoeverstate == 21)
2397     {
2398       if (theta > -10 && theta < 10)
2399       {
2400         manoeverstate = 0;
2401       }
2402     }
2403 
2404     float pulljoystick = 0.005;
2405     float nocorrection = 0.1;
2406     if (recrolleffect > rolleffect + nocorrection) rolleffect += pulljoystick * timestep;
2407     else if (recrolleffect < rolleffect - nocorrection) rolleffect -= pulljoystick * timestep;
2408     if (recelevatoreffect > elevatoreffect + nocorrection) elevatoreffect += pulljoystick * timestep;
2409     else if (recelevatoreffect < elevatoreffect - nocorrection) elevatoreffect -= pulljoystick * timestep;
2410     return;
2411   }
2412   else
2413   {
2414     if (ai)
2415       easymodel = 1;
2416   }
2417 
2418   // calculate the recommended height, recheight2 depends on it
2419   if (manoeverheight > 0) manoeverheight -= dt;
2420   if (manoeverheight <= 0)
2421   {
2422     if (!(id >= FIGHTER1 && id <= FIGHTER2) && target != NULL) // no fighter, has target (missile, mine)
2423     {
2424       recheight = target->tl->y - targetheight;
2425     }
2426     else if (id == FIGHTER_TRANSPORT || id == FIGHTER_TRANSPORT2) // transporters have to stay higher
2427     {
2428       recheight = 40; manoeverheight = 1;
2429     }
2430     else if (id >= FIGHTER1 && id <= FIGHTER2 && target != NULL) // fighter, has target
2431     {
2432       if (target->id >= FIGHTER1 && target->id <= FIGHTER2)
2433         recheight = target->tl->y - targetheight;  // target is a fighter
2434       else
2435         recheight = target->tl->y - targetheight + 5; // target is no fighter
2436       if (!l->isWater (l->f [flyxs] [flyzs])) // not flying above water
2437       {
2438         if (recheight < 3.5 + 0.01 * aggressivity)
2439           recheight = 3.5 + 0.01 * aggressivity;
2440       }
2441       float minh = 5.5 + 0.01 * aggressivity; // minimum height
2442       if (l->type == LAND_CANYON) minh = 6.5 + 0.01 * aggressivity; // stay higher in canyons
2443       if (fabs (tl->y - myheight) < minh)
2444       {
2445         recheight = 9 + 0.015 * aggressivity;
2446         if (fabs (tl->y - myheight) < minh * 0.3)
2447         {
2448           manoeverstate = 10;
2449           display ("Manoever: Vertical climb", LOG_ALL);
2450         }
2451         else
2452         {
2453           manoeverheight = 5 * timestep; // fly manoever to gain height
2454         }
2455       }
2456       if (disttarget < 50 && fabs (tl->y - myheight) > 25)
2457       {
2458         recheight = 8 + 0.025 * aggressivity;
2459         manoeverheight = 12 * timestep;
2460       }
2461     }
2462   }
2463 
2464   if (ttl <= 0 && id >= MISSILE1 && id <= MISSILE2 && id != MISSILE_MINE1)
2465   { recheight = -100; recheight2 = -100; recgamma = 90; }
2466   else if (ai)
2467   {
2468     if (target != NULL && ((id >= MISSILE1 && id <= MISSILE2) || (id >= FIGHTER1 && id <= FIGHTER2 && manoeverheight <= 0))) // is AGM
2469     {
2470       float dgamma = 0;
2471       if (disttarget <= -0.00001 || disttarget >= 0.00001) // no division by zero
2472         dgamma = atan ((target->tl->y - tl->y) / disttarget) * 180 / PI - (gamma - 180);
2473       recgamma = gamma + dgamma; // get recommended elevation to target
2474     }
2475     else
2476     {
2477       recgamma = (int) ((recheight2 - tl->y) * 10 - gamma + 360);
2478     }
2479   }
2480 
2481 
2482 
2483 
2484 
2485   // do a smooth roll
2486   float deltatheta;
2487 
2488   if (easymodel == 1)
2489   {
2490     deltatheta = rectheta - theta;
2491     if (fabs (dtheta) > 30)
2492     { dtheta = 0; }
2493     float mynimbility = fabs (deltatheta) / 5.0F * nimbility;
2494     if (mynimbility > nimbility) mynimbility = nimbility;
2495     float nimbility2 = mynimbility;
2496     if (nimbility2 >= -0.00001 && nimbility2 <= 0.00001)
2497       nimbility2 = 0.00001;
2498 
2499     if (deltatheta > 0 && dtheta < 0) dtheta += mynimbility * timefac;
2500     else if (deltatheta < 0 && dtheta > 0) dtheta -= mynimbility * timefac;
2501     else if (deltatheta > 0)
2502     {
2503       float estimatedtheta = dtheta * (dtheta + nimbility2 * 5 / timefac) / 2 / nimbility2;
2504       if (deltatheta > estimatedtheta) dtheta += mynimbility * timefac;
2505       else if (deltatheta < estimatedtheta) dtheta -= mynimbility * timefac;
2506     }
2507     else
2508     {
2509       float estimatedtheta = -dtheta * (dtheta - nimbility2 * 5 / timefac) / 2 / nimbility2;
2510       if (deltatheta < estimatedtheta) dtheta -= mynimbility * timefac;
2511       else if (deltatheta > estimatedtheta) dtheta += mynimbility * timefac;
2512     }
2513     if (dtheta > (nimbility * (1.0 + realspeed)) * timefac * 5.0F)
2514       dtheta = (nimbility * (1.0 + realspeed)) * timefac * 5.0F;
2515     theta += dtheta;
2516 
2517     // height changes
2518     if (easymodel == 1)
2519     {
2520       float nimbility1 = nimbility / 5;
2521       if (nimbility1 >= -0.00001 && nimbility1 <= 0.00001)
2522         nimbility1 = 0.00001;
2523       if (theta > maxtheta) theta = maxtheta; // restrict roll angle
2524       else if (theta < -maxtheta) theta = -maxtheta;
2525 
2526       float deltagamma = recgamma - gamma;
2527       if (deltagamma > 0 && dgamma < 0) dgamma += nimbility1 * timefac;
2528       else if (deltagamma < 0 && dgamma > 0) dgamma -= nimbility1 * timefac;
2529       else if (deltagamma > 0)
2530       {
2531         float estimatedgamma = dgamma * (dgamma + nimbility1 * 2) / nimbility1;
2532         if (id == 200)
2533           id = id;
2534         if (deltagamma > estimatedgamma + 2) dgamma += nimbility1 * timefac;
2535         else if (deltagamma < estimatedgamma - 2) dgamma -= nimbility1 * timefac;
2536       }
2537       else if (deltagamma < 0)
2538       {
2539         float estimatedgamma = -dgamma * (dgamma + nimbility1 * 2) / nimbility1;
2540         if (id == 200)
2541           id = id;
2542         if (deltagamma < estimatedgamma - 2) dgamma -= nimbility1 * timefac;
2543         else if (deltagamma > estimatedgamma + 2) dgamma += nimbility1 * timefac;
2544       }
2545       if (dgamma > manoeverability * (3.33 + 15.0 * realspeed) * timefac)
2546         dgamma = manoeverability * (3.33 + 15.0 * realspeed) * timefac;
2547       gamma += dgamma;
2548     }
2549   }
2550 
2551   if (gamma > 180 + maxgamma) gamma = 180 + maxgamma;
2552   else if (gamma < 180 - maxgamma) gamma = 180 - maxgamma;
2553 
2554   if (id >= MISSILE1 && id <= MISSILE2)
2555   {
2556     if (target == NULL)
2557     {
2558       ttl = 0;
2559       return;
2560     }
2561     else if (target->active == false)
2562     {
2563       ttl = 0;
2564       return;
2565     }
2566   }
2567 
2568   if (target == NULL) return;
2569 
2570   // fighter's targeting mechanism for missiles
2571   if (id >= FIGHTER1 && id <= FIGHTER2) // for fighters do the following
2572   {
2573     if (ai)
2574     {
2575       if (target->id >= FIGHTER1 && target->id <= FIGHTER2)
2576       {
2577         if (!selectMissileAirFF (m))
2578           selectMissileAir (m);
2579       }
2580       else
2581       {
2582         selectMissileGround (m);
2583       }
2584     }
2585   }
2586 
2587   if (!ai || target == NULL) // no AI (player) or no target found, then exit
2588   {
2589     return;
2590   }
2591 
2592   int firerate;
2593   if (difficulty == 0) firerate = 12;
2594   else if (difficulty == 1) firerate = 6;
2595   else firerate = 3;
2596 
2597   float dx2, dz2, ex, ez;
2598   float dx = target->tl->x - tl->x, dz = target->tl->z - tl->z; // current distances
2599   if ((id >= FIGHTER1 && id <= FIGHTER2) || (id >= MISSILE1 && id <= MISSILE2) || (id >= FLAK1 && id <= FLAK2) || (id >= TANK1 && id <= TANK2))
2600   {
2601     float t = 10.0 * disttarget; // generous time to new position
2602     if (t > 60) t = 60; // higher values will not make sense
2603     t *= (float) (400 - precision) / 400;
2604     int tt = (int) target->theta;
2605     if (tt < 0) tt += 360;
2606     float newphi = t * SIN(tt) * 5.0 * target->manoeverability; // new angle of target after time t
2607     if (newphi > 90) newphi = 90;
2608     else if (newphi < -90) newphi = -90;
2609     newphi += (float) target->phi;
2610     if (newphi >= 360) newphi -= 360;
2611     if (newphi < 0) newphi += 360;
2612     if ((id >= FIGHTER1 && id <= FIGHTER2) || (id >= FLAK1 && id <= FLAK2) || (id >= TANK1 && id <= TANK2))
2613     {
2614       ex = target->tl->x - SIN(newphi) * t * target->realspeed * 0.25; // estimated target position x
2615       ez = target->tl->z - COS(newphi) * t * target->realspeed * 0.25; // estimated target position z
2616     }
2617     else
2618     {
2619       ex = target->tl->x - SIN(newphi) * t * target->realspeed * 0.05; // estimated target position x
2620       ez = target->tl->z - COS(newphi) * t * target->realspeed * 0.05; // estimated target position z
2621     }
2622     dx2 = ex - tl->x; dz2 = ez - tl->z; // estimated distances
2623   }
2624   else
2625   {
2626     dx2 = dx; dz2 = dz;
2627   }
2628   float a, w = phi;
2629   if (dz2 > -0.0001 && dz2 < 0.0001) dz2 = 0.0001;
2630 
2631   // get heading to target
2632   a = atan (dx2 / dz2) * 180 / PI;
2633   if (dz2 > 0)
2634   {
2635     if (dx2 > 0) a -= 180.0F;
2636     else a += 180.0F;
2637   }
2638 //    this->aw = a;
2639   aw = a - w; // aw=0: target in front, aw=+/-180: target at back
2640   if (aw < -180) aw += 360;
2641   if (aw > 180) aw -= 360;
2642 
2643   if (manoevertheta > 0) manoevertheta -= dt;
2644   if (manoeverthrust > 0) manoeverthrust -= dt;
2645 
2646   // heading calculations
2647   if (id >= FIGHTER1 && id < FIGHTER_TRANSPORT) // for fighters do the following
2648   {
2649     if (!acttype && disttarget <= 1000 && manoevertheta <= 0) // no special action, near distance, no roll manoever
2650     {
2651       if (aw > 0) // positive angle
2652       {
2653         if (aw > 140 && disttarget > 50)
2654         {
2655           manoeverstate = 1;
2656           display ("Manoever: Immelmann", LOG_ALL);
2657         }
2658         else if (aw > 160.0F + 0.05 * intelligence && disttarget < 4 + 0.01 * intelligence) // target very near at the back
2659         {
2660           manoeverstate = 1;
2661           display ("Manoever: Immelmann", LOG_ALL);
2662         }
2663         else if (aw > 160 && disttarget < 25) // target is at the back
2664         {
2665           if (fabs (tl->y - myheight) > 7 && gamma >= 175 + intelligence / 100) // high enough over ground
2666           {
2667             manoeverstate = 20; // roll
2668             display ("Manoever: Roll", LOG_ALL);
2669           }
2670           else
2671           {
2672             rectheta = -90;
2673             if (manoevertheta <= 0)
2674             {
2675               manoevertheta = timestep * (100 + myrandom ((400 - intelligence) / 8)); // turn hard left or right
2676               display ("Manoever: Turn", LOG_ALL);
2677             }
2678             if (manoeverthrust <= 0)
2679               recthrust = maxthrust / (1.05F + (float) intelligence * 0.0015); // fly faster
2680             if (intelligence < 280 && manoeverheight <= 0)
2681             {
2682               recheight = 5; manoeverheight = timestep * (20 - intelligence / 50);
2683               display ("Manoever: Height change", LOG_ALL);
2684             } // stay low
2685           }
2686         }
2687         else if (aw < 40 && disttarget > 60)
2688         {
2689           rectheta = 0;
2690         }
2691         else if (aw < 20 && disttarget > 30)
2692         {
2693           rectheta = 0;
2694         }
2695         else // otherwise fly to target direction
2696         {
2697           int maw = aw > 90 ? 90 : (int) aw;
2698           int maw2 = 90 - maw;
2699           rectheta = 90 - maw2 * intelligence / 400;
2700           if (maw < 30) rectheta /= 2;
2701           if (maw < 5)
2702           {
2703             rectheta = 0;
2704             if (target->id >= FIGHTER1 && target->id <= FIGHTER2 && disttarget < 20)
2705               ((AIObj *) target)->manoevertheta = timestep * (50 - intelligence / 10);
2706           }
2707         }
2708       }
2709       else // same for negative angle
2710       {
2711         if (aw < -140 && disttarget > 50)
2712         {
2713           manoeverstate = 1;
2714           display ("Manoever: Immelmann", LOG_ALL);
2715         }
2716         else if (aw < -160.0F - 0.05 * intelligence && disttarget < 4 + 0.01 * intelligence) // target very near at the back
2717         {
2718           manoeverstate = 1;
2719           display ("Manoever: Immelmann", LOG_ALL);
2720         }
2721         else if (aw < -160 && disttarget < 25)
2722         {
2723           if (fabs (tl->y - myheight) > 7 && gamma >= 175 + intelligence / 100) // high enough over ground
2724           {
2725             manoeverstate = 20; // roll
2726             display ("Manoever: Roll", LOG_ALL);
2727           }
2728           else
2729           {
2730             rectheta = 90;
2731             if (manoevertheta <= 0)
2732             {
2733               manoevertheta = timestep * (100 + myrandom ((400 - intelligence) / 8));
2734               display ("Manoever: Turn", LOG_ALL);
2735             }
2736             if (manoeverthrust <= 0)
2737               recthrust = maxthrust / (1.05F + (float) intelligence * 0.0015);
2738             if (intelligence < 280 && manoeverheight <= 0)
2739             {
2740               recheight = 5; manoeverheight = timestep * (20 - intelligence / 50);
2741               display ("Manoever: Height change", LOG_ALL);
2742             }
2743           }
2744         }
2745         else if (aw > -40 && disttarget > 60)
2746         {
2747           rectheta = 0;
2748         }
2749         else if (aw > -20 && disttarget > 30)
2750         {
2751           rectheta = 0;
2752         }
2753         else
2754         {
2755           int maw = aw < -90 ? -90 : (int) aw;
2756           int maw2 = -90 - maw;
2757           rectheta = -90 - maw2 * intelligence / 400;
2758           if (maw > -30) rectheta /= 2;
2759           if (maw > -5)
2760           {
2761             rectheta = 0;
2762             if (target->id >= FIGHTER1 && target->id <= FIGHTER2 && disttarget < 20)
2763               ((AIObj *) target)->manoevertheta = timestep * (50 - intelligence / 10);
2764           }
2765         }
2766       }
2767     }
2768   }
2769   else if (id >= MISSILE1 && id <= MISSILE2) // for missiles do the following
2770   {
2771     if (fabs (aw) < 50 && disttarget > 50) // target in front and minimum distance, then no roll
2772       rectheta = 0;
2773     else // otherwise chase target
2774     {
2775       if (aw < -90 || aw > 90) rectheta = 0;
2776       else if (aw > 0)
2777       {
2778         rectheta = aw > 90 ? 90 : aw;
2779       }
2780       else
2781       {
2782         rectheta = aw < -90 ? -90 : aw;
2783       }
2784     }
2785   }
2786   else if (id >= FLAK1 && id <= FLAK2) // ground-air-cannon
2787   {
2788     recthrust = 0; thrust = 0;
2789     if (aw > 5)
2790     {
2791       rectheta = maxtheta;
2792     }
2793     else if (aw < -5)
2794     {
2795       rectheta = -maxtheta;
2796     }
2797     else
2798     {
2799       rectheta = 0;
2800     }
2801   }
2802   else if (id >= TANK1 && id <= TANK2) // tanks
2803   {
2804     recthrust = maxthrust; thrust = maxthrust; // always at maximum thrust
2805     if (aw > 5)
2806     {
2807       rectheta = maxtheta;
2808     }
2809     else if (aw < -5)
2810     {
2811       rectheta = -maxtheta;
2812     }
2813     else
2814     {
2815       rectheta = 0;
2816     }
2817     if (firecannonttl <= 0)
2818     {
2819       if (id == TANK_AIR1)
2820         if (fabs (rectheta - theta) < 2 && fabs (aw) < 20 && disttarget < 40 && target->tl->y > tl->y + 2)
2821         {
2822           fireCannon (c);
2823           firecannonttl += firerate * timestep;
2824         }
2825       if (id == TANK_GROUND1)
2826         if (fabs (rectheta - theta) < 2 && fabs (aw) < 20 && disttarget < 35 && target->tl->y <= tl->y + 1 && target->tl->y >= tl->y - 1)
2827         {
2828           fireCannon (c);
2829           firecannonttl += firerate * timestep;
2830         }
2831     }
2832   }
2833 
2834   // thrust and manoever calculations
2835   if (id >= FIGHTER1 && id <= FIGHTER2) // fighters
2836   {
2837     if (disttarget > 5 + aggressivity / 12) // 2.5 seems to be best, but fighters become far too strong
2838     {
2839       if (disttarget < 50 && fabs (aw) > 30 && manoeverthrust <= 0)
2840         recthrust = maxthrust / (1.0F + (float) intelligence * 0.0025);
2841       else thrustUp (); // otherwise fly faster
2842     }
2843     else if (manoeverthrust <= 0)
2844     {
2845       if (recthrust > target->thrust) // adopt thrust of target
2846       {
2847         thrustDown ();
2848       }
2849       else
2850       {
2851         thrustUp ();
2852       }
2853     }
2854     if (disttarget > 50 && fabs (aw) < 20) // high distance and target in front, then fly straight
2855       rectheta = 0;
2856     if (manoeverthrust <= 0)
2857       if (disttarget < 25 && fabs (aw) > 160 && target->id >= TANK1) // avoid collisions
2858       {
2859         manoeverthrust = 25 * timestep;
2860         recthrust = maxthrust;
2861         if (difficulty == 0) recthrust = maxthrust * 0.8F;
2862         manoevertheta = 25 * timestep;
2863         rectheta = 0;
2864         manoeverheight = 25 * timestep;
2865         recheight = 10;
2866       }
2867     // fire cannon?
2868     float agr = 4.0 - (float) aggressivity / 100;
2869     if (firecannonttl <= 0)
2870     {
2871       if (fabs (rectheta - theta) < agr && fabs (aw) < 20 + agr * 4 && disttarget < 30)
2872         fireCannon (c);
2873       else if (disttarget < 2 + agr && fabs (aw) < 20 + agr * 4)
2874         fireCannon (c);
2875     }
2876     // fire missile?
2877     if (firemissilettl <= 0)
2878     {
2879       if (target->id >= FIGHTER1 && target->id <= FIGHTER2)
2880       {
2881         int z1 = 0;
2882         if (disttarget < 15 && fabs (aw) < 20)
2883         {
2884           for (i = 0; i < maxfighter; i ++)
2885           {
2886             if (target == f [i]->target && party == f [i]->party) z1 ++;
2887           }
2888           if (z1 >= 3)
2889           {
2890             manoevertheta = 15 * timestep;
2891             firemissilettl = 10 * timestep;
2892             if (myrandom (2)) rectheta = 90;
2893             else rectheta = -90;
2894             targetNextEnemy (f);
2895           }
2896         }
2897         if (firemissilettl <= 0)
2898         {
2899           if (fabs (rectheta - theta) < agr / 2 && fabs (aw) < agr && disttarget < 45)
2900           {
2901             fireMissile (m, (AIObj *) target);
2902             firemissilettl += aggressivity * timestep;
2903           }
2904         }
2905       }
2906       else // ground target
2907       {
2908         if (fabs (rectheta - theta) < 5 + agr * 4 && fabs (aw) < 5 + agr * 4 && disttarget < 50)
2909           if (!(l->lsticker & 7))
2910           {
2911             fireMissileGround (m);
2912             firemissilettl += aggressivity / 2 * timestep;
2913           }
2914       }
2915     }
2916   }
2917 
2918   if ((id >= FLAK1 && id <= FLAK2) || id == SHIP_CRUISER || id == SHIP_DESTROYER1 || id == TANK_TRSAM1)
2919   {
2920     if (firecannonttl <= 0)
2921     for (int i = 0; i < maxfighter; i ++)
2922     if (f [i]->active)
2923     if (party != f [i]->party)
2924     {
2925       disttarget = distance (f [i]); // distance to target
2926       ex = f [i]->tl->x; // estimated target position x
2927       ez = f [i]->tl->z; // estimated target position z
2928       dx2 = ex - tl->x; dz2 = ez - tl->z; // estimated distances
2929       w = (int) phi;
2930       if (dz2 > -0.0001 && dz2 < 0.0001) dz2 = 0.0001;
2931       a = (atan (dx2 / dz2) * 180 / PI);
2932       if (dz2 > 0)
2933       {
2934         if (dx2 > 0) a -= 180;
2935         else a += 180;
2936       }
2937       aw = a - w;
2938       if (aw < -180) aw += 360;
2939       if (aw > 180) aw -= 360;
2940       if (id == FLAK_AIR1)
2941         if (f [i]->tl->y > tl->y + 2)
2942         {
2943           if (fabs (aw) <= 20 && disttarget < 50) // + aggressive
2944             fireCannon (c, phi + aw);
2945           firecannonttl = firerate * timestep;
2946         }
2947       if (id == SHIP_DESTROYER1)
2948         if (f [i]->tl->y > tl->y + 2)
2949         {
2950           if (aw >= 0 && aw < 40 && disttarget < 50) // + aggressive
2951             fireCannon (c, phi + aw);
2952           if (aw >= -40 && aw < 0 && disttarget < 50) // + aggressive
2953             fireCannon (c, phi + aw);
2954           if (aw >= 120 && aw < 160 && disttarget < 50) // + aggressive
2955             fireCannon (c, phi + aw);
2956           if (aw >= -160 && aw < -120 && disttarget < 50) // + aggressive
2957             fireCannon (c, phi + aw);
2958           firecannonttl = firerate * timestep;
2959         }
2960       if (firemissilettl <= 0)
2961         if (id == FLARAK_AIR1)
2962           if (fabs (aw) < 25 && disttarget < 45) // + aggressive
2963             if (f [i]->tl->y > tl->y + 2)
2964             {
2965               ttf = 0;
2966               fireMissileAirFF (m, f [i]);
2967               firemissilettl += (20 + firerate * 10) * timestep;
2968             }
2969       if (id == TANK_TRSAM1)
2970       {
2971         if (firemissilettl <= 0)
2972           if (aw >= -30 && aw < 30 && disttarget < 60) // + aggressive
2973           {
2974             ttf = 0;
2975             fireMissileAirFF (m, f [i]);
2976             firemissilettl += aggressivity / 5 * timestep;
2977             missiles [6] ++; // unlimited ammo
2978           }
2979       }
2980       if (id == SHIP_CRUISER)
2981       {
2982         if (firemissilettl <= 0)
2983           if (aw >= -30 && aw < 30 && disttarget < 60) // + aggressive
2984           {
2985             ttf = 0;
2986             fireMissileAirFF (m, f [i]);
2987             firemissilettl += aggressivity / 5 * timestep;
2988             missiles [6] ++; // unlimited ammo
2989           }
2990         if (firecannonttl <= 0)
2991         {
2992           fireCannon (c, phi + aw);
2993         }
2994       }
2995     }
2996   }
2997   if (id >= FIGHTER1 && id <= FIGHTER2)
2998   {
2999     if (rectheta > 90 - precision / 5) rectheta = 90 - precision / 5;
3000     else if (rectheta < -90 + precision / 5) rectheta = -90 + precision / 5;
3001   }
3002 }
3003 
3004 #endif
3005