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