/* GL-117 Copyright 2001, 2002 Thomas A. Drexl aka heptargon This file is part of GL-117. GL-117 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GL-117 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GL-117; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* This file includes all AI objects instancing models. */ #ifndef IS_AIOBJECT_H #include "aiobject.h" #include "glland.h" #include "main.h" #include "mathtab.h" // disabled int DynamicObj::net_write () { net [0] = '.'; int z = 1; memcpy (&net [z], &tl->x, sizeof (&tl->x)); z += sizeof (&tl->x); memcpy (&net [z], &tl->y, sizeof (&tl->y)); z += sizeof (&tl->y); memcpy (&net [z], &tl->z, sizeof (&tl->z)); z += sizeof (&tl->z); memcpy (&net [z], &phi, sizeof (&phi)); z += sizeof (&phi); memcpy (&net [z], &theta, sizeof (&theta)); z += sizeof (&theta); memcpy (&net [z], &gamma, sizeof (&gamma)); z += sizeof (&gamma); return z; } // disabled void DynamicObj::net_read () { int z = 1; memcpy (&tl->x, &net [z], sizeof (&tl->x)); z += sizeof (&tl->x); memcpy (&tl->y, &net [z], sizeof (&tl->y)); z += sizeof (&tl->y); memcpy (&tl->z, &net [z], sizeof (&tl->z)); z += sizeof (&tl->z); memcpy (&phi, &net [z], sizeof (&phi)); z += sizeof (&phi); memcpy (&theta, &net [z], sizeof (&theta)); z += sizeof (&theta); memcpy (&gamma, &net [z], sizeof (&gamma)); z += sizeof (&gamma); } void DynamicObj::activate () { active = true; draw = true; } void DynamicObj::deactivate () { active = false; draw = false; } void DynamicObj::dinit () { rot->a = 90; phi = 0; theta = 0; gamma = 180; rectheta = 0; tl->z = 0; tl->x = 0; forcex = 0; forcez = 0; forcey = 0; maxthrust = 0.3; braking = 0/*0.99*/; manoeverability = 0.5; thrust = maxthrust; recthrust = thrust; recheight = 5.0; ttl = -1; shield = 0.01F; maxshield = 0.01F; immunity = 0; recgamma = 180; id = CANNON1; impact = 7; source = NULL; points = 0; party = 0; easymodel = 1; // easy model elevatoreffect = 0; ruddereffect = 0; rolleffect = 0; maxgamma = 70; maxtheta = 90; gamma = 180; theta = 0; explode = 0; sink = 0; nimbility = 1.0; fighterkills = 0; shipkills = 0; tankkills = 0; otherkills = 0; killed = false; realism = false; accx = accy = accz = 0; } DynamicObj::DynamicObj () { dinit (); } DynamicObj::DynamicObj (Space *space2, CModel *o2, float zoom2) { this->space = space2; o = o2; zoom = zoom2; dinit (); space->addObject (this); } void DynamicObj::thrustUp () { recthrust += maxthrust / 12; if (recthrust > maxthrust) recthrust = maxthrust; } void DynamicObj::thrustDown () { recthrust -= maxthrust / 12; if (recthrust < maxthrust / 2) recthrust = maxthrust / 2; } float DynamicObj::distance (DynamicObj *target) { float dx = target->tl->x - tl->x; float dz = target->tl->z - tl->z; float dy = target->tl->y - tl->y; return sqrt (dx * dx + dz * dz + dy * dy); } float DynamicObj::distanceXZ (DynamicObj *target) { float dx = target->tl->x - tl->x; float dz = target->tl->z - tl->z; return sqrt (dx * dx + dz * dz); } // check whether the object is exploding or sinking and deactivate if necessary void DynamicObj::checkExplosion (Uint32 dt) { if (explode > 0) { if (explode == 1) { ttl = -1; if (id == STATIC_CONTAINER1 || id == STATIC_RADAR1 || id == STATIC_COMPLEX1) { setExplosion (1.5, 40 * timestep); setBlackSmoke (3.0, 80 * timestep); } else if (id == STATIC_OILRIG1) { setExplosion (3.0, 40 * timestep); setBlackSmoke (5.5, 80 * timestep); } else if (id == STATIC_TENT1) { } else if (id == TANK1) { } else { float zoom2 = zoom * 2; if (zoom2 > 2) zoom2 = 2; setExplosion (zoom2, 35 * timestep); setBlackSmoke (1.0, 60 * timestep); } } if (id >= STATIC_GROUND || (id >= MOVING_GROUND && id <= MOVING_WATER)) { if (explode >= 25 * timestep && ttl == -1) { setExplosion (zoom * 2, 35 * timestep); setBlackSmoke (1.0, 60 * timestep); ttl = -2; } if (explode >= 30 * timestep && ttl == -2) { setExplosion (zoom * 2, 35 * timestep); setBlackSmoke (1.0, 60 * timestep); ttl = -3; } } if (explode >= 35 * timestep) { deactivate (); ttl = -1; explode += dt; // must be > 35*timestep to end mission if (id >= STATIC_GROUND || (id >= MOVING_GROUND && id <= MOVING_WATER)) { explode = 0; draw = true; id = STATIC_PASSIVE; shield = 100000; o = &model_rubble1; zoom *= 0.7F; if (zoom > 1) zoom = 1; tl->y = l->getExactHeight (tl->x, tl->z) + zoom / 4; } } else { explode += dt; } } if (sink) { sink += dt; if (sink > 100 * timestep) { deactivate (); ttl = -1; } } } // check the objects shield value and explode/sink if necessary void DynamicObj::checkShield () { if (shield <= 0) { shield = 0; if (explode <= 0) { if (id >= MISSILE1 && id <= MISSILE2) { explode = 1; active = false; } if (id >= FIGHTER1 && id <= FIGHTER2) { explode = 1; active = false; } if (id >= TANK1 && id <= TANK2) { explode = 1; active = false; } if (id >= FLAK1 && id <= FLAK2) { explode = 1; active = false; } if (id >= STATIC_PASSIVE) { explode = 1; active = false; } } if (sink <= 0) if (id >= SHIP1 && id <= SHIP2) { sink = 1; } } } // check whether the object collides on the ground and alter gamma and y-translation void DynamicObj::crashGround (Uint32 dt) { if (id >= MOVING_GROUND) return; float height = tl->y - l->getExactHeight (tl->x, tl->z); if (height < zoom) { tl->y -= (height - zoom); gamma += 10; if (shield > 0) { if (id >= MISSILE1 && id <= MISSILE2) { setExplosion (1.2, 30 * timestep); setBlackSmoke (1.2, 30 * timestep); } if (id >= FIGHTER1 && id <= FIGHTER2) { setExplosion (0.2, 25 * timestep); setBlackSmoke (0.5, 25 * timestep); } } if (id >= CANNON1 && id <= CANNON2) deactivate (); float decfac = 3.0F; if (this == (DynamicObj *) fplayer && game == GAME_PLAY) { if (difficulty == 1) decfac = 6.0F; else if (difficulty == 2) decfac = 15.0F; } if (realism && this == (DynamicObj *) fplayer && game == GAME_PLAY) shield = -1; else shield -= decfac * dt / timestep; } // restrict to a maximum height, we want an action game!!! a little bit more now 50 -> 80 if (height > 80) tl->y = l->getHeight (tl->x, tl->z) + 80; } // check for collision, simplified model, each model is surrounded by a cube // this works pretty well, but we must use more than one model for complex models or scenes void DynamicObj::collide (DynamicObj *d, Uint32 dt) // d must be the medium (laser, missile) { if (immunity > 0 || d->immunity > 0) return; if (explode > 0 || sink > 0) return; bool collide = false; if (tl->x + o->cubex >= d->tl->x - d->o->cubex && tl->x - o->cubex <= d->tl->x + d->o->cubex && tl->y + o->cubey >= d->tl->y - d->o->cubey && tl->y - o->cubey <= d->tl->y + d->o->cubey && tl->z + o->cubez >= d->tl->z - d->o->cubez && tl->z - o->cubez <= d->tl->z + d->o->cubez) { collide = true; } if (collide) { if (this == (DynamicObj *) fplayer && game == GAME_PLAY && realism && d->id >= AIR && d->id < MOVING_GROUND) { shield = -1.0F; // player collision vs another plane in SIM mode, boom d->shield = -1.0F; } if (id < STATIC_PASSIVE || (id >= STATIC_PASSIVE && d->id >= MISSILE1 && d->id <= MISSILE2)) shield -= (float) d->impact; else shield -= 2.0F; d->shield -= (float) impact; if (d->source != NULL && active) // only for missiles/cannons { if (d->source->party != party) // calculate points { if (maxshield < 2000) d->source->points += (int) impact; // extra points for shooting an enemy object } else { d->source->points -= (int) impact; // subtract points for shooting an own object } if (shield <= 0) if (d->source->party != party && active && draw && !killed) if (d->source->id >= FIGHTER1 && d->source->id <= FIGHTER2) { killed = true; if (id >= FIGHTER1 && id <= FIGHTER2) d->source->fighterkills ++; else if (id >= SHIP1 && id <= SHIP2) d->source->shipkills ++; else if ((id >= FLAK1 && id <= FLAK2) || (id >= TANK1 && id <= TANK2)) d->source->tankkills ++; else d->source->otherkills ++; } } setExplosion (0.2, 20 * timestep); setBlackSmoke (0.5, 30 * timestep); } } void DynamicObj::setExplosion (float maxzoom, int len) { int i; for (i = 0; i < maxexplosion; i ++) // search a free explosion instance if (explosion [i]->ttl <= 0) break; if (i >= maxexplosion) i = 0; explosion [i]->setExplosion (tl->x, tl->y, tl->z, forcex, forcey, forcez, maxzoom, len); } void DynamicObj::setBlackSmoke (float maxzoom, int len) { int i; for (i = 0; i < maxblacksmoke; i ++) // search a free blacksmoke instance if (blacksmoke [i]->ttl <= 0) break; if (i >= maxblacksmoke) i = 0; blacksmoke [i]->setBlackSmoke (tl->x, tl->y, tl->z, phi, maxzoom, len); } // return heading difference towards enemy int DynamicObj::getAngle (DynamicObj *o) { float dx2 = o->tl->x - tl->x, dz2 = o->tl->z - tl->z; int a, w = (int) phi; if (dz2 > -0.0001 && dz2 < 0.0001) dz2 = 0.0001; a = (int) (atan (dx2 / dz2) * 180 / PI); if (dz2 > 0) { if (dx2 > 0) a -= 180; else a += 180; } int aw = a - w; if (aw < -180) aw += 360; if (aw > 180) aw -= 360; return aw; } // return elevation difference towards enemy int DynamicObj::getAngleH (DynamicObj *o) { float disttarget = distance (o); return (int) (atan ((o->tl->y - tl->y) / disttarget) * 180 / PI - (gamma - 180)); } // check for a looping, this is tricky :-) bool DynamicObj::checkLooping () { if (gamma > 270) { gamma = 540 - gamma; theta += 180; phi += 180; rectheta += 180; if (theta >= 360) theta -= 360; if (rectheta >= 360) rectheta -= 360; if (phi >= 360) phi -= 360; return true; } else if (gamma < 90) { gamma = 180 - gamma; theta += 180; phi += 180; rectheta += 180; if (theta >= 360) theta -= 360; if (rectheta >= 360) rectheta -= 360; if (phi >= 360) phi -= 360; return true; } return false; } // discrete movement, called about timestep times per second, timer-dependant, currently without extra thread (GLUT)! void DynamicObj::move (Uint32 dt) { if (dt <= 0) return; if (realspeed <= 0) realspeed = 1.0F; float brakepower = 1.0F; float timefac = (float) dt / (float) timestep; checkExplosion (dt); // check if this object is exploding if (sink > 0) // only ships (they will not explode) { tl->y -= 0.02 * timefac; // sink down gamma = recgamma = 180.0 + 0.5 * (float) sink / timestep; // change angle when sinking return; // and exit move() } if (!active && !draw) return; // exit if not active if (id >= STATIC_PASSIVE) // only buildings, static objects { if (id == STATIC_TENT1) theta = 178; // set the correct angles to diplay rot->setAngles ((short) (90 + gamma - 180), (short) theta + 180, (short) -phi); checkShield (); return; // and exit this function } if (id == FLARE1) // only flares { tl->y -= 0.04 * timefac; // fall down (gravity, constant) zoom = 0.12F + 0.03F * sin ((float) ttl / (float) timestep / 15); // blink (high frequency) phi = camphi; // angles to viewer (player) theta = 0; gamma = camgamma; } if (id == CHAFF1) // only chaff { tl->y -= 0.04 * timefac; // fall down (gravity, constant) zoom = 0.12F + 0.01F * (80 * timestep - ttl) / timestep; // spread out phi = camphi; // angles to viewer (player) theta = 0; gamma = camgamma; } // check maximum gamma if (easymodel == 1) { if (gamma > 180 + maxgamma) gamma = 180 + maxgamma; else if (gamma < 180 - maxgamma) gamma = 180 - maxgamma; } else if (easymodel == 2) // otherwise check for value overflow due to loops { (void) checkLooping (); } // the core of directional alterations and force calculations: // easymodel==1 means to change heading due to roll angle // this may seem complete nonsense for fighters, but it is just a simplification!!! // angle / aileron = constant, thats enough for a simple AI if (easymodel == 1) { if (id >= MOVING_GROUND) { phi += SIN(theta) * manoeverability * 667 * timefac; //10.0 * maxthrust / div; } else { phi += SIN(theta) * manoeverability * (3.33 + 15.0 * realspeed) * timefac; gamma -= fabs (SIN(theta) * COS(gamma) / realspeed / 20) * timefac; // realistic modification if (gamma < 180 - maxgamma) gamma = 180 - maxgamma; } } else if (easymodel == 2) // now this is much more general, however simplified: { int vz = 1; if (gamma < 90 || gamma > 270) vz = -1; // change heading and elevation due to ailerons and rudder if (maxthrust + thrust <= -0.00001 || maxthrust + thrust >= 0.00001) { phi += vz * SIN(theta) * elevatoreffect * manoeverability * (3.33 + 15.0 * realspeed) * timefac; gamma += COS(theta) * elevatoreffect * manoeverability * (3.33 + 15.0 * realspeed) * timefac; phi += -vz * COS(theta) * ruddereffect * manoeverability * (0.66 + 3.0 * realspeed) * timefac; gamma += SIN(theta) * ruddereffect * manoeverability * (0.66 + 3.0 * realspeed) * timefac; if (!realism) gamma -= fabs (SIN(theta) * COS(gamma) / realspeed / 20) * timefac; // realistic modification } // change roll due to roll ;-) if (rolleffect) { theta += rolleffect * (nimbility * (1.0 + realspeed)) * timefac * 5.0F; rectheta = theta; } } if (phi < 0) phi += 360.0; // validate heading else if (phi >= 360.0) phi -= 360.0; if (easymodel == 1) // easy model restrictions { if (rectheta > maxtheta) rectheta = maxtheta; else if (rectheta < -maxtheta) rectheta = -maxtheta; if (recgamma > 180 + maxgamma) recgamma = 180 + maxgamma; else if (recgamma < 180 - maxgamma) recgamma = 180 - maxgamma; } else if (easymodel == 2) { if (theta < -180 && rectheta < -180) { rectheta += 360; theta += 360; } else if (theta >= 180 && rectheta >= 180) { rectheta -= 360; theta -= 360; } } if (recthrust > maxthrust) // check maximum throttle recthrust = maxthrust; float throttlechange = maxthrust / 200 * timefac; if (recthrust > thrust + throttlechange) // alter throttle effect slowly { thrust += throttlechange; } else if (recthrust < thrust - throttlechange) { thrust -= throttlechange; } // PHYSICS (simplified model) CVector3 vaxis, uaxis, utemp, utemp2, utemp3; float gammaup, phiup, thetaup; bool stop; float gravityforce; if (id <= CANNON2) { tl->x += forcex * timefac; // add our vector to the translation tl->z += forcez * timefac; tl->y += forcey * timefac; goto cannondone; // jump down to decrease ttl and test collision } // axis pointing through the fighter's nose vaxis.set (COS(gamma) * SIN(phi), SIN(gamma), COS(gamma) * COS(phi)); if (realism) { // axis pointing upwards through the cockpit gammaup = gamma + 90; thetaup = -theta; phiup = phi; uaxis.set (COS(gammaup) * SIN(phiup), SIN(gammaup), COS(gammaup) * COS(phiup)); // upward axis (theta = 0) // now rotate around vaxis using theta utemp.take (&uaxis); utemp.mul (COS(thetaup)); utemp2.take (&vaxis); utemp2.mul ((1 - COS(thetaup)) * uaxis.dotproduct (&vaxis)); utemp3.take (&uaxis); utemp3.crossproduct (&vaxis); utemp3.mul (SIN(thetaup)); utemp.add (&utemp2); utemp.add (&utemp3); uaxis.take (&utemp); } realspeed = sqrt (forcex * forcex + forcez * forcez + forcey * forcey); if (realism) // sim model { // add drag force braking = (fabs (ruddereffect) + fabs (elevatoreffect) * 4 + fabs (rolleffect)) * realspeed / 50; brakepower = pow (0.93 - braking, timefac); accx *= brakepower; accy *= brakepower; accz *= brakepower; // add throttle force accz += thrust * vaxis.z * 0.3 * timefac; accx += thrust * vaxis.x * 0.3 * timefac; accy -= thrust * vaxis.y * 0.3 * timefac; // add elevation force accz += thrust * uaxis.z * 0.067 * timefac; accx += thrust * uaxis.x * 0.067 * timefac; accy -= thrust * uaxis.y * 0.067 * timefac; // add gravity force accy -= 0.015 * timefac; // add our vector to the translation float stepfac = 0.24; tl->x += accx * timefac * stepfac; tl->z += accz * timefac * stepfac; tl->y += accy * timefac * stepfac; float scalef = 1.1; forcex = accx * stepfac * scalef; forcey = accy * stepfac * scalef; forcez = accz * stepfac * scalef; } else // action model { // and correct the speedvector forcez = vaxis.z * realspeed; forcex = vaxis.x * realspeed; forcey = -vaxis.y * realspeed; // add throttle force forcez += thrust * vaxis.z * 0.01 * timefac; //0.03 and braking=0.97 by try and error forcex += thrust * vaxis.x * 0.01 * timefac; forcey -= thrust * vaxis.y * 0.01 * timefac; gravityforce = sqrt (realspeed) * vaxis.y * 0.0012 * timefac; forcez += gravityforce * vaxis.z; forcex += gravityforce * vaxis.x; forcey -= gravityforce * vaxis.y; // drag force simulated by adjusting the vector if (easymodel == 2) braking = (fabs (ruddereffect) + fabs (elevatoreffect)) * realspeed / 50; else braking = (fabs (theta / 45)) * realspeed / 50; brakepower = pow (0.9915 - braking, timefac); forcex *= brakepower; forcez *= brakepower; forcey *= brakepower; } stop = false; if (id >= TANK1 && id <= TANK2) // tanks cannot climb steep faces { float newy = l->getExactHeight (tl->x + forcex, tl->z + forcez) + zoom / 2; if (fabs (newy - tl->y) > 0.05) stop = true; else if (fabs (newy - tl->y) > 2) stop = false; } if (!realism) if (!stop) { tl->x += forcex * timefac; // add our vector to the translation tl->z += forcez * timefac; tl->y += forcey * timefac; } // calculate the objects real thrust only once realspeed = sqrt (forcex * forcex + forcez * forcez + forcey * forcey); // objects moving on the ground should always change their elevation due to the surface if (id >= TANK1 && id <= TANK2 && thrust > 0 && !stop) { float newy = l->getExactHeight (tl->x, tl->z) + zoom / 2; float dy = newy - tl->y + forcey; float dx = fabs (forcex) + fabs (forcez); float gamma2 = 0; if (fabs (dx) > 0.0001) gamma2 = atan (dy / dx); gamma = 180.0 + 180.0 / PI * gamma2; tl->y = newy; } if (id != ASTEROID) { // set angles to correctly display the object rot->setAngles ((short) (90 + gamma - 180), (short) theta + 180, (short) -phi); } else // asteroids should rotate around their center of weight, as we must not change theta/gamma, we do this here { ttl -= dt; // we use the ttl value as timer, for other methods ttl<0 is the same as ttl=-1 if (ttl <= -360 * timestep) ttl = -1; int rot1 = (int) (sin ((zoom - 1.3) * 8) * 4); int rot2 = (int) (cos ((zoom - 1.3) * 8) * 4); rot->setAngles ((short) (90 + gamma + ttl * rot1 / timestep - 180), (short) theta + ttl * rot2 / timestep + 180, (short) -phi); } cannondone:; if (ttl > 0) { ttl -= dt; // decrease time to live if (ttl <= 0) { ttl = -1; if (id >= MISSILE1 && id <= MISSILE2) recheight = -10; // missiles drop else deactivate (); // cannon shots vanish } } checkShield (); // check shield issues crashGround (dt); // check ground collision if (immunity > 0) immunity -= dt; // decrease immunity } void AIObj::aiinit () { int i; acttype = 0; dualshot = false; intelligence = 100; aggressivity = 100; precision = 100; shield = 0.01F; ai = true; active = true; draw = true; target = NULL; dtheta = 0; dgamma = 0; id = MISSILE1; impact = 30; manoevertheta = 0; manoeverheight = 0; manoeverthrust = 0; idle = 0; smokettl = 0; firecannonttl = 0; firemissilettl = 0; fireflarettl = 0; firechaffttl = 0; flares = 0; aw = 0; source = NULL; points = 0; easymodel = 1; elevatoreffect = 0; ruddereffect = 0; gamma = 180; recgamma = 180; dgamma = 0; theta = 0; maxgamma = 70; maxtheta = 90; missiletype = 0; autofire = false; ttl = -1; ttf = 30 * timestep; score = -1; for (i = 0; i < missiletypes; i ++) missiles [i] = 0; for (i = 0; i < missileracks; i ++) { missilerack [i] = -1; missilerackn [i] = 0; } bomber = 0; timer = 0; ammo = -1; manoeverstate = 0; } void AIObj::missileCount () { if (id < FIGHTER1 || id > FIGHTER2) return; int i; for (i = 0; i < missiletypes; i ++) missiles [i] = 0; for (i = 0; i < missileracks; i ++) { if (missilerackn [i] > 0) { missiles [missilerack [i]] += missilerackn [i]; } } } void AIObj::newinit (int id, int party, int intelligence, int precision, int aggressivity) { int i; ai = true; this->id = id; this->party = party; manoeverstate = 0; activate (); for (i = 0; i < missileracks; i ++) missilerackn [i] = 0; ammo = -1; bomber = 0; dualshot = false; float cubefac = 0.6F; // fighter float cubefac1 = 0.7F; // tanks and sams o = getModel (id); o->cubex = zoom; o->cubey = zoom; o->cubez = zoom; if (id == FIGHTER_FALCON) { maxthrust = 0.31; nimbility = 0.86; manoeverability = 0.48; maxshield = 85; zoom = 0.35; maxtheta = 90.0; maxgamma = 70.0; missilerackn [0] = 2; missilerackn [1] = 2; missilerackn [2] = 2; missilerackn [3] = 2; missilerack [0] = 0; missilerack [1] = 6; missilerack [2] = 6; missilerack [3] = 0; flares = 20; chaffs = 20; statfirepower = 1; ammo = 1200; } else if (id == FIGHTER_SWALLOW) { maxthrust = 0.24; nimbility = 0.64; manoeverability = 0.35; maxshield = 110; zoom = 0.43; maxtheta = 90.0; maxgamma = 70.0; missilerackn [0] = 2; missilerackn [1] = 3; missilerackn [2] = 3; missilerackn [3] = 2; missilerack [0] = 6; missilerack [1] = 3; missilerack [2] = 3; missilerack [3] = 6; flares = 20; chaffs = 20; bomber = 1; statfirepower = 3; ammo = 1200; } else if (id == FIGHTER_HAWK) { maxthrust = 0.26; nimbility = 0.72; manoeverability = 0.42; maxshield = 120; zoom = 0.43; maxtheta = 90.0; maxgamma = 70.0; missilerackn [0] = 1; missilerackn [1] = 3; missilerackn [2] = 3; missilerackn [3] = 1; missilerack [0] = 6; missilerack [1] = 3; missilerack [2] = 3; missilerack [3] = 6; flares = 20; chaffs = 20; bomber = 1; statfirepower = 2; ammo = 1200; } else if (id == FIGHTER_HAWK2) { maxthrust = 0.28; nimbility = 0.75; manoeverability = 0.44; maxshield = 140; zoom = 0.45; maxtheta = 90.0; maxgamma = 70.0; missilerackn [0] = 1; missilerackn [1] = 3; missilerackn [2] = 3; missilerackn [3] = 1; missilerack [0] = 6; missilerack [1] = 4; missilerack [2] = 4; missilerack [3] = 6; flares = 20; chaffs = 20; bomber = 1; statfirepower = 3; ammo = 1400; dualshot = true; } else if (id == FIGHTER_TRANSPORT) { maxthrust = 0.14; maxshield = 45; missiles [0] = 0; nimbility = 0.15; manoeverability = 0.05; impact = 5; zoom = 1.5; maxgamma = 25; maxtheta = 30; flares = 0; chaffs = 0; ammo = 0; } else if (id == FIGHTER_TRANSPORT2) { maxthrust = 0.16; maxshield = 35; missiles [0] = 0; nimbility = 0.12; manoeverability = 0.04; impact = 5; zoom = 1.5; maxgamma = 25; maxtheta = 30; flares = 0; chaffs = 0; ammo = 0; } else if (id == FIGHTER_BUZZARD) { maxthrust = 0.31; nimbility = 0.82; manoeverability = 0.46; maxshield = 75; zoom = 0.44; maxtheta = 90.0; maxgamma = 70.0; missilerackn [0] = 2; missilerackn [1] = 2; missilerackn [2] = 2; missilerackn [3] = 2; missilerack [0] = 0; missilerack [1] = 6; missilerack [2] = 6; missilerack [3] = 0; flares = 20; chaffs = 20; statfirepower = 2; ammo = 1200; } else if (id == FIGHTER_CROW) { maxthrust = 0.25; nimbility = 0.72; manoeverability = 0.4; maxshield = 60; zoom = 0.41; maxtheta = 90.0; maxgamma = 70.0; missilerackn [0] = 1; missilerackn [1] = 2; missilerackn [2] = 2; missilerackn [3] = 1; missilerack [0] = 6; missilerack [1] = 0; missilerack [2] = 0; missilerack [3] = 6; flares = 20; chaffs = 20; statfirepower = 1; ammo = 1000; } else if (id == FIGHTER_STORM) { maxthrust = 0.25; nimbility = 0.52; manoeverability = 0.34; maxshield = 160; zoom = 0.45; maxtheta = 90.0; maxgamma = 70.0; missilerackn [0] = 1; missilerackn [1] = 2; missilerackn [2] = 2; missilerackn [3] = 1; missilerack [0] = 6; missilerack [1] = 0; missilerack [2] = 0; missilerack [3] = 6; flares = 25; chaffs = 25; statfirepower = 4; ammo = 1800; } else if (id == FIGHTER_PHOENIX) { maxthrust = 0.3; nimbility = 0.54; manoeverability = 0.34; maxshield = 180; zoom = 0.47; maxtheta = 90.0; maxgamma = 70.0; missilerackn [0] = 3; missilerackn [1] = 3; missilerackn [2] = 3; missilerackn [3] = 3; missilerack [0] = 4; missilerack [1] = 4; missilerack [2] = 4; missilerack [3] = 4; flares = 25; chaffs = 25; bomber = 1; statfirepower = 5; ammo = 2000; dualshot = true; } else if (id == FIGHTER_REDARROW) { maxthrust = 0.33; nimbility = 0.95; manoeverability = 0.52; maxshield = 120; zoom = 0.4; maxtheta = 90.0; maxgamma = 70.0; missilerackn [0] = 2; missilerackn [1] = 3; missilerackn [2] = 3; missilerackn [3] = 2; missilerack [0] = 7; missilerack [1] = 1; missilerack [2] = 1; missilerack [3] = 7; flares = 25; chaffs = 25; statfirepower = 2; ammo = 1400; dualshot = true; } else if (id == FIGHTER_BLACKBIRD) { maxthrust = 0.3; nimbility = 1.0; manoeverability = 0.54; maxshield = 85; zoom = 0.33; maxtheta = 90.0; maxgamma = 70.0; missilerackn [0] = 2; missilerackn [1] = 3; missilerackn [2] = 3; missilerackn [3] = 2; missilerack [0] = 7; missilerack [1] = 1; missilerack [2] = 1; missilerack [3] = 7; flares = 25; chaffs = 25; statfirepower = 2; ammo = 1400; dualshot = true; } if (id >= FIGHTER1 && id <= FIGHTER2) { recthrust = maxthrust / 2.0; shield = maxshield; thrust = recthrust = maxthrust / 2; smoke->type = 1; impact = 2; forcez = recthrust; o->cubex = zoom * cubefac; o->cubey = zoom * cubefac; o->cubez = zoom * cubefac; } if (id == FLAK_AIR1) { maxthrust = 0; thrust = 0; maxgamma = 0; maxtheta = 0.03; manoeverability = 12.0; shield = maxshield = 80; zoom = 0.35; } if (id == FLARAK_AIR1) { maxthrust = 0; thrust = 0; maxgamma = 0; maxtheta = 0.03; manoeverability = 6.0; shield = maxshield = 70; zoom = 0.3; missiles [6] = 100; } if (id >= FLAK1 && id <= FLAK2) { o->cubex = zoom * cubefac1; o->cubey = zoom * cubefac1; o->cubez = zoom * cubefac1; } if (id == TANK_AIR1) { maxthrust = 0.04; thrust = 0; gamma = 180; theta = 0; phi = 0; maxgamma = 0; maxtheta = 0.03; manoeverability = 8.0; shield = maxshield = 160; zoom = 0.35; o->cubex = zoom * 0.7; o->cubey = zoom * 0.45; o->cubez = zoom * 0.7; } else if (id == TANK_GROUND1) { maxthrust = 0.04; thrust = 0; gamma = 180; theta = 0; phi = 0; maxgamma = 0; maxtheta = 0.03; manoeverability = 8.0; shield = maxshield = 200; zoom = 0.4; o->cubex = zoom * 0.7; o->cubey = zoom * 0.5; o->cubez = zoom * 0.7; } else if (id == TANK_PICKUP1) { maxthrust = 0; thrust = 0.02; maxgamma = 0; maxtheta = 0.03; manoeverability = 0; shield = maxshield = 30; zoom = 0.25; o->cubex = zoom * 0.7; o->cubey = zoom * 0.55; o->cubez = zoom * 0.7; } else if (id == TANK_TRUCK1) { maxthrust = 0; thrust = 0.02; maxgamma = 0; maxtheta = 0.03; manoeverability = 0; shield = maxshield = 20; zoom = 0.45; o->cubex = zoom * 0.6; o->cubey = zoom * 0.35; o->cubez = zoom * 0.6; } else if (id == TANK_TRUCK2) { maxthrust = 0; thrust = 0.02; maxgamma = 0; maxtheta = 0.03; manoeverability = 0; shield = maxshield = 40; zoom = 0.4; o->cubex = zoom * 0.6; o->cubey = zoom * 0.35; o->cubez = zoom * 0.6; } else if (id == TANK_TRSAM1) { maxthrust = 0; thrust = 0.02; maxgamma = 0; maxtheta = 0.03; manoeverability = 0; shield = maxshield = 50; zoom = 0.35; missiles [6] = 200; o->cubex = zoom * 0.7; o->cubey = zoom * 0.6; o->cubez = zoom * 0.7; } if (id >= TANK1 && id <= TANK2) { } if (id == SHIP_CRUISER) { zoom = 5.0; maxthrust = 0.05; thrust = 0.05; maxgamma = 0; maxtheta = 0.03; manoeverability = 4.0; impact = 20; shield = maxshield = 5500; missiles [6] = 200; o->cubex = zoom * 0.35; o->cubey = zoom * 0.1; o->cubez = zoom * 0.35; } else if (id == SHIP_DESTROYER1) { zoom = 2.5; maxthrust = 0.05; thrust = 0.05; maxgamma = 0; maxtheta = 0.03; manoeverability = 6.0; impact = 20; shield = maxshield = 2800; o->cubex = zoom * 0.4; o->cubey = zoom * 0.12; o->cubez = zoom * 0.4; } float missilethrustbase = 1.2F; if (id == MISSILE_AIR1) { intelligence = 100; maxthrust = 0.7 * missilethrustbase; nimbility = 2.5; // old 2.2 manoeverability = 1.5; ttl = 300 * timestep; impact = 35; } else if (id == MISSILE_AIR2) { intelligence = 50; maxthrust = 0.75 * missilethrustbase; nimbility = 3.5; // old 3.5 manoeverability = 2.0; ttl = 320 * timestep; impact = 45; } else if (id == MISSILE_AIR3) { intelligence = 0; maxthrust = 0.8 * missilethrustbase; nimbility = 4.5; manoeverability = 2.5; ttl = 340 * timestep; impact = 55; } else if (id == MISSILE_GROUND1) { intelligence = 50; maxthrust = 0.75 * missilethrustbase; nimbility = 1.2; manoeverability = 1.0; ai = true; ttl = 300 * timestep; impact = 400; } else if (id == MISSILE_GROUND2) { intelligence = 0; maxthrust = 0.8 * missilethrustbase; nimbility = 1.5; manoeverability = 1.0; ai = true; ttl = 400 * timestep; impact = 500; } else if (id == MISSILE_DF1) { intelligence = 0; maxthrust = 0.75 * missilethrustbase; nimbility = 0.0; manoeverability = 0.0; ai = true; ttl = 350 * timestep; impact = 920; } else if (id == MISSILE_FF1) { intelligence = 0; maxthrust = 0.8 * missilethrustbase; nimbility = 2.0; manoeverability = 1.3; ttl = 300 * timestep; impact = 40; } else if (id == MISSILE_FF2) { intelligence = 0; maxthrust = 0.85 * missilethrustbase; nimbility = 3.0; manoeverability = 2.0; ttl = 320 * timestep; impact = 50; } else if (id == MISSILE_MINE1) { intelligence = 0; maxthrust = 0.1; if (difficulty == 1) maxthrust = 0.14; else if (difficulty == 2) maxthrust = 0.18; nimbility = 1.5; manoeverability = 1.0; ai = true; ttl = -1; impact = 500; zoom = 0.3; } if (id >= MISSILE1 && id <= MISSILE2) { o->cubex = zoom; o->cubey = zoom; o->cubez = zoom; } if (id >= STATIC_PASSIVE) { intelligence = 0; maxthrust = 0; nimbility = 0; manoeverability = 0; impact = 5; maxtheta = 0; maxgamma = 0; } if (id == STATIC_TENT1) { shield = maxshield = 80; zoom = 0.5; o->cubex = zoom * 0.9; o->cubey = zoom; o->cubez = zoom * 0.9; } if (id == STATIC_TENT4) { shield = maxshield = 160; zoom = 1.2; o->cubex = zoom * 0.7; o->cubey = zoom * 0.42; o->cubez = zoom * 0.7; } if (id == STATIC_CONTAINER1) { shield = maxshield = 30; zoom = 1.0; impact = 20; o->cubex = zoom * 0.4; o->cubey = zoom * 0.35; o->cubez = zoom * 0.9; } if (id == STATIC_HALL1) { shield = maxshield = 450; zoom = 1.8; impact = 20; o->cubex = zoom * 0.45; o->cubey = zoom * 0.42; o->cubez = zoom; } if (id == STATIC_HALL2) { shield = maxshield = 900; zoom = 2.5; impact = 20; o->cubex = zoom; o->cubey = zoom * 0.45; o->cubez = zoom; } if (id == STATIC_OILRIG1) { shield = maxshield = 1400; zoom = 3.5; impact = 20; o->cubex = zoom * 0.95; o->cubey = zoom * 0.5; o->cubez = zoom * 0.95; } if (id == STATIC_COMPLEX1) { shield = maxshield = 5000; zoom = 2.0; impact = 20; o->cubex = zoom * 0.75; o->cubey = zoom * 0.6; o->cubez = zoom * 0.75; } if (id == STATIC_RADAR1) { shield = maxshield = 500; zoom = 1.3; impact = 20; o->cubex = zoom * 0.5; o->cubey = zoom * 0.7; o->cubez = zoom * 0.5; } if (id == ASTEROID) { shield = maxshield = 100000; zoom = 0.01 * myrandom (60) + 1.0; impact = 5; thrust = 0.25; maxthrust = 0.25; forcez = 0.12; ai = false; o->cubex = zoom * 0.7; o->cubey = zoom * 0.7; o->cubez = zoom * 0.7; } if (id == STATIC_BASE1) { shield = maxshield = 5500; zoom = 4.0; impact = 20; o->cubex = zoom * 0.7; o->cubey = zoom * 0.5; o->cubez = zoom * 0.7; } if (id == STATIC_DEPOT1) { shield = maxshield = 3000; zoom = 1.5; impact = 20; o->cubex = zoom; o->cubey = zoom * 0.5; o->cubez = zoom; } if (id == STATIC_BARRIER1) { shield = maxshield = 1000; zoom = 12.0; impact = 2000; o->cubex = 0.8; o->cubey = 11; o->cubez = 11; } if (id >= STATIC_PASSIVE) { } if (difficulty == 0) // easy { intelligence = 400 - (400 - intelligence) * 1 / 3; precision = 400 - (400 - precision) * 1 / 3; aggressivity = 400 - (400 - aggressivity) * 1 / 3; if (party != 1 && shield > 10) // not player party { shield = shield * 8 / 10; maxshield = shield; } } else if (difficulty == 1) // normal { intelligence = 400 - (400 - intelligence) * 2 / 3; precision = 400 - (400 - precision) * 2 / 3; aggressivity = 400 - (400 - aggressivity) * 2 / 3; } else if (difficulty == 2) // hard { } this->intelligence = intelligence; this->precision = precision; this->aggressivity = aggressivity; missileCount (); } void AIObj::newinit (int id, int party, int intelligence) { newinit (id, party, intelligence, intelligence, intelligence); } AIObj::AIObj () { o = NULL; zoom = 1.0; aiinit (); smoke = new CSmoke (0); } AIObj::AIObj (Space *space2, CModel *o2, float zoom2) { this->space = space2; o = o2; zoom = zoom2; aiinit (); smoke = new CSmoke (0); space->addObject (this); } AIObj::~AIObj () { delete smoke; } void AIObj::initValues (DynamicObj *dobj, float phi) { float fac = zoom / 8; if (dobj->id == FLARE1 || dobj->id == CHAFF1) fac = -fac; // use the exact polar coordinates because of gamma and theta float cgamma = gamma; dobj->tl->x = tl->x + COS(cgamma) * SIN(phi) * fac; dobj->tl->y = tl->y - SIN(cgamma) * fac; if ((id >= FLAK1 && id <= FLAK2) || (id >= TANK1 && id <= TANK2)) dobj->tl->y += fac; dobj->tl->z = tl->z + COS(cgamma) * COS(phi) * fac; dobj->phi = phi; dobj->rectheta = dobj->theta; dobj->forcex = forcex; dobj->forcey = forcey; dobj->forcez = forcez; dobj->rot->setAngles ((short) (90 + dobj->gamma - 180), (short) dobj->theta + 180, (short) -dobj->phi); } void AIObj::fireCannon (DynamicObj *laser, float phi) { if (firecannonttl > 0) return; if (ammo == 0) return; ammo --; laser->thrust = 0; laser->recthrust = laser->thrust; laser->manoeverability = 0.0; laser->maxthrust = 0; if (target != NULL && ai) { if (target->active) { // exact calculation to hit enemy (non-static turret!) if (id >= FIGHTER1 && id <= FIGHTER2) laser->gamma = gamma; else laser->gamma = 180.0 + atan ((target->tl->y - tl->y) / distance (target)) * 180.0 / pitab; } } else laser->gamma = gamma; // + 90.0; laser->party = party; laser->ttl = 80 * timestep; laser->shield = 1; laser->immunity = (int) (zoom * 12) * timestep; laser->source = this; laser->phi = phi; laser->theta = theta; initValues (laser, phi); float fac = 0.7F; laser->forcex += COS(laser->gamma) * SIN(laser->phi) * fac; laser->forcey -= SIN(laser->gamma) * fac; laser->forcez += COS(laser->gamma) * COS(laser->phi) * fac; laser->activate (); firecannonttl += 45; if (day) { if (dualshot) laser->o = &model_cannon1b; else laser->o = &model_cannon1; } else { if (dualshot) laser->o = &model_cannon2b; else laser->o = &model_cannon2; } } void AIObj::fireCannon (DynamicObj **laser, float phi) { int i; if (firecannonttl > 0) return; if (ammo == 0) return; for (i = 0; i < maxlaser; i ++) { if (!laser [i]->active) break; } if (i < maxlaser) { fireCannon (laser [i], phi); } } void AIObj::fireCannon (DynamicObj **laser) { if (firecannonttl > 0) return; if (ammo == 0) return; fireCannon (laser, phi); } void AIObj::fireMissile2 (int id, AIObj *missile, AIObj *target) { char buf [STDSIZE]; if (debuglevel == LOG_ALL) { sprintf (buf, "Missile: party=%d, id=%d", party, id); display (buf, LOG_ALL); } ttf = 50 * timestep; missile->dinit (); missile->aiinit (); missile->newinit (id, party, 0); initValues (missile, phi); missile->id = id; missile->explode = 0; missile->thrust = thrust + 0.001; missile->recthrust = missile->maxthrust; missile->gamma = gamma; missile->target = target; missile->recgamma = gamma; missile->shield = 1; missile->party = party; missile->immunity = (45 + (int) (zoom * 6.0)) * timestep; missile->dtheta = 0; missile->dgamma = 0; missile->source = this; missile->activate (); if (id >= FIGHTER1 && id <= FIGHTER2) { missile->manoeverheight = 30 * timestep; missile->recheight = missile->tl->y - l->getHeight (missile->tl->x, missile->tl->z) - 4; } } void AIObj::fireFlare2 (DynamicObj *flare) { char buf [STDSIZE]; if (debuglevel == LOG_ALL) { sprintf (buf, "Flare: party=%d", party); display (buf, debuglevel); } flare->dinit (); flare->thrust = 0; flare->realspeed = 0; flare->recthrust = 0; flare->manoeverability = 0.0; flare->maxthrust = 1.0; flare->gamma = 0; flare->party = party; flare->ttl = 80 * timestep; flare->shield = 1; flare->immunity = (int) (zoom * 12) * timestep; flare->source = this; flare->phi = phi; flare->id = FLARE1; initValues (flare, phi); flare->activate (); flare->explode = 0; } void AIObj::fireChaff2 (DynamicObj *chaff) { char buf [STDSIZE]; if (debug == LOG_ALL) { sprintf (buf, "Chaff: party=%d", party); display (buf, LOG_ALL); } chaff->dinit (); chaff->thrust = 0; chaff->realspeed = 0; chaff->recthrust = 0; chaff->manoeverability = 0.0; chaff->maxthrust = 1.0; chaff->gamma = 0; chaff->party = party; chaff->ttl = 80 * timestep; chaff->shield = 1; chaff->immunity = (int) (zoom * 12) * timestep; chaff->source = this; chaff->phi = phi; chaff->id = CHAFF1; initValues (chaff, phi); chaff->activate (); chaff->explode = 0; chaff->zoom = 0.12F; } int AIObj::firstMissile () { int i = 0; while (!missiles [i]) { i ++; if (i >= missiletypes) return 0; } ttf = 50 * timestep; return i; } int AIObj::nextMissile (int from) { int i = from + 1; if (i >= missiletypes) i = 0; while (!missiles [i]) { i ++; if (i >= missiletypes) i = 0; if (i == from) break; } ttf = 50 * timestep; return i; } bool AIObj::haveMissile (int id) { char buf [STDSIZE]; id -= MISSILE1; if (id < 0 || id >= missiletypes) { sprintf (buf, "Wrong missile ID in %s, line %d", __FILE__, __LINE__); display (buf, LOG_ERROR); } if (missiles [id] > 0) return true; return false; } bool AIObj::haveMissile () // due to missiletype { if (missiles [missiletype] > 0) return true; return false; } void AIObj::decreaseMissile (int id) { char buf [STDSIZE]; int i; id -= MISSILE1; if (id < 0 || id >= missiletypes) { sprintf (buf, "Wrong missile ID in %s, line %d", __FILE__, __LINE__); display (buf, LOG_ERROR); } missiles [id] --; int ptrrack = 0, maxrack = 0; for (i = 0; i < missileracks; i ++) if (missilerack [i] == id) if (missilerackn [i] > maxrack) { ptrrack = i; maxrack = missilerackn [i]; } if (maxrack > 0) { missilerackn [ptrrack] --; refscale [ptrrack * 3 + 2 - missilerackn [ptrrack]] = 0; } } bool AIObj::fireMissile (int id, AIObj **missile, AIObj *target) { int i; if (!haveMissile (id)) return false; if (ttf > 0) return false; for (i = 0; i < maxmissile; i ++) { if (missile [i]->ttl <= 0) break; } if (i < maxmissile) { fireMissile2 (id, missile [i], target); decreaseMissile (id); firemissilettl = 20 * timestep; return true; } return false; } bool AIObj::fireMissile (AIObj **missile, AIObj *target) { if (ttf > 0) return false; return fireMissile (missiletype + MISSILE1, missile, (AIObj *) target); } bool AIObj::fireMissile (int id, AIObj **missile) { if (ttf > 0) return false; return fireMissile (id, missile, (AIObj *) target); } bool AIObj::fireMissile (AIObj **missile) { if (ttf > 0) return false; return fireMissile (missiletype + MISSILE1, missile); } bool AIObj::fireFlare (DynamicObj **flare, AIObj **missile) { int i, i2; if (flares <= 0) return false; if (fireflarettl > 0) return false; for (i = 0; i < maxflare; i ++) { if (flare [i]->ttl <= 0) break; } if (i < maxflare) { fireFlare2 (flare [i]); flares --; fireflarettl = 8 * timestep; for (i2 = 0; i2 < maxmissile; i2 ++) { if (missile [i2]->ttl > 0) { if (missile [i2]->id >= MISSILE_AIR1 && missile [i2]->id <= MISSILE_AIR3) // only heat seeking missiles if (missile [i2]->target == this) // only change target if angle is good { bool hit = false; if (easymodel == 1) { if (myrandom ((int) (theta + 20)) > 50) hit = true; } else if (easymodel == 2) { if (myrandom ((int) (fabs (elevatoreffect) * 90 + 20)) > 50) hit = true; } if (hit) { if (debuglevel == LOG_ALL) { display ("Missile to flare", LOG_ALL); } missile [i2]->target = flare [i]; } } } } return true; } return false; } bool AIObj::fireChaff (DynamicObj **chaff, AIObj **missile) { int i, i2; if (chaffs <= 0) return false; if (firechaffttl > 0) return false; for (i = 0; i < maxchaff; i ++) { if (chaff [i]->ttl <= 0) break; } if (i < maxchaff) { fireChaff2 (chaff [i]); chaffs --; firechaffttl = 8 * timestep; for (i2 = 0; i2 < maxmissile; i2 ++) { if (missile [i2]->ttl > 0) { if (missile [i2]->id > MISSILE_AIR3) // only radar seeking missiles if (missile [i2]->target == this) // only change target if angle is good { bool hit = false; if (easymodel == 1) { if (myrandom ((int) (theta + 20)) > 50) hit = true; } else if (easymodel == 2) { if (myrandom ((int) (fabs (elevatoreffect) * 90 + 20)) > 50) hit = true; } if (hit) { if (debuglevel == LOG_ALL) { display ("Missile to chaff", LOG_ALL); } missile [i2]->target = chaff [i]; } } } } return true; } return false; } bool AIObj::fireMissileAir (AIObj **missile, AIObj *target) { if (ttf > 0) return false; if (target->id >= MOVING_GROUND) return false; if (haveMissile (MISSILE_AIR3)) return fireMissile (MISSILE_AIR3, missile, (AIObj *) target); else if (haveMissile (MISSILE_AIR2)) return fireMissile (MISSILE_AIR2, missile, (AIObj *) target); else if (haveMissile (MISSILE_AIR1)) return fireMissile (MISSILE_AIR1, missile, (AIObj *) target); return false; } bool AIObj::selectMissileAir (AIObj **missile) { bool sel = false; if (haveMissile (MISSILE_AIR3)) { missiletype = MISSILE_AIR3 - MISSILE1; sel = true; } else if (haveMissile (MISSILE_AIR2)) { missiletype = MISSILE_AIR2 - MISSILE1; sel = true; } else if (haveMissile (MISSILE_AIR1)) { missiletype = MISSILE_AIR1 - MISSILE1; sel = true; } return sel; } bool AIObj::fireMissileAirFF (AIObj **missile, AIObj *target) { if (ttf > 0) return false; if (target->id >= MOVING_GROUND) return false; if (haveMissile (MISSILE_FF2)) return fireMissile (MISSILE_FF2, missile, (AIObj *) target); else if (haveMissile (MISSILE_FF1)) return fireMissile (MISSILE_FF1, missile, (AIObj *) target); return false; } bool AIObj::selectMissileAirFF (AIObj **missile) { bool sel = false; if (haveMissile (MISSILE_FF2)) { missiletype = MISSILE_FF2 - MISSILE1; sel = true; } else if (haveMissile (MISSILE_FF1)) { missiletype = MISSILE_FF1 - MISSILE1; sel = true; } return sel; } bool AIObj::fireMissileGround (AIObj **missile) { if (ttf > 0) return false; if (target->id < MOVING_GROUND) return false; if (haveMissile (MISSILE_GROUND2)) return fireMissile (MISSILE_GROUND2, missile, (AIObj *) target); else if (haveMissile (MISSILE_GROUND1)) return fireMissile (MISSILE_GROUND1, missile, (AIObj *) target); return false; } bool AIObj::selectMissileGround (AIObj **missile) { bool sel = false; if (haveMissile (MISSILE_GROUND2)) { missiletype = MISSILE_GROUND2 - MISSILE1; sel = true; } else if (haveMissile (MISSILE_GROUND1)) { missiletype = MISSILE_GROUND1 - MISSILE1; sel = true; } return sel; } void AIObj::targetNearestGroundEnemy (AIObj **f) { int i; float d = 1E12; //10000 is too low ttf = 50 * timestep; for (i = 0; i < maxfighter; i ++) { if (this != f [i] && party != f [i]->party && f [i]->active) { float phi = getAngle (f [i]); float d2 = distance (f [i]) * (60 + fabs (phi)); // prefer enemies in front if (bomber) if (f [i]->id < MOVING_GROUND) d2 += 1E10; // only use this target if no ground targets exist if (d2 < d) { d = d2; target = f [i]; } } } if (target) if (distance (target) > 400) { target = NULL; } } void AIObj::targetNearestEnemy (AIObj **f) { int i; float d = 1E12; //10000 is too low ttf = 50 * timestep; for (i = 0; i < maxfighter; i ++) { if (this != f [i] && party != f [i]->party && f [i]->active) { float phi = getAngle (f [i]); float d2 = distance (f [i]) * (60 + fabs (phi)); // prefer enemies in front if (d2 < d) { d = d2; target = f [i]; } } } if (!ai && target) if (distance (target) > 400) target = NULL; } void AIObj::targetLockingEnemy (AIObj **f) { int i; ttf = 50 * timestep; if (target == NULL) target = f [0]; for (i = 0; i < maxfighter; i ++) if (target == f [i]) break; int z = 0; do { i ++; if (i >= maxfighter) { i = 0; z ++; } } while ((!f [i]->active || f [i]->party == party || f [i]->target != this || distance (f [i]) > 200) && z <= 1); target = f [i]; if (z > 1 && !ai) target = NULL; } void AIObj::targetNext (AIObj **f) { int i; ttf = 50 * timestep; if (target == NULL) target = f [0]; for (i = 0; i < maxfighter; i ++) if (target == f [i]) break; int z = 0; do { i ++; if (i >= maxfighter) i = 0; if (f [i] == this) { i ++; z ++; } if (i >= maxfighter) i = 0; } while ((!f [i]->active || distance (f [i]) > 400) && z <= 1); target = f [i]; if (z > 1 && !ai) target = NULL; } void AIObj::targetNextEnemy (AIObj **f) { int i; ttf = 50 * timestep; if (target == NULL) target = f [0]; for (i = 0; i < maxfighter; i ++) if (target == f [i]) break; int z = 0; do { i ++; if (i >= maxfighter) i = 0; if (f [i] == this) { i ++; z ++; } if (i >= maxfighter) i = 0; } while ((!f [i]->active || distance (f [i]) > 400 || party == f [i]->party) && z <= 1); target = f [i]; if (z > 1 && !ai) target = NULL; } void AIObj::targetPrevious (AIObj **f) { int i; ttf = 50 * timestep; if (target == NULL) target = f [0]; for (i = 0; i < maxfighter; i ++) if (target == f [i]) break; int z = 0; do { i --; if (i < 0) i = maxfighter - 1; if (f [i] == this) { i --; z ++; } if (i < 0) i = maxfighter - 1; } while ((!f [i]->active || distance (f [i]) > 400) && z <= 1); target = f [i]; if (z > 1 && !ai) target = NULL; } // core AI method void AIObj::aiAction (Uint32 dt, AIObj **f, AIObj **m, DynamicObj **c, DynamicObj **flare, DynamicObj **chaff) { int i; timer += dt; if (!active && !draw) // not active, not drawn, then exit { return; } if (firecannonttl > 0) firecannonttl -= dt; // time to fire the next missile if (firemissilettl > 0) firemissilettl -= dt; // time to fire the next missile if (fireflarettl > 0) fireflarettl -= dt; // time to fire the next flare if (firechaffttl > 0) firechaffttl -= dt; // time to fire the next chaff if (smokettl > 0) smokettl -= dt; // time to fire the next chaff // move object according to our physics move (dt); float timefac = (float) dt / (float) timestep; if (id >= STATIC_PASSIVE) // no AI for static ground objects (buildings) return; // set smoke if ((id >= MISSILE1 && id < MISSILE_MINE1) || (id >= FIGHTER1 && id <= FIGHTER2)) // missile or fighter { float sz = COS(gamma) * COS(phi) * zoom * 1.1; // polar (spherical) coordinates float sy = -SIN(gamma) * zoom * 1.1; float sx = COS(gamma) * SIN(phi) * zoom * 1.1; // some smoke elements per discrete movement float fg = sqrt (forcex * forcex + forcey * forcey + forcez * forcez) * 13; if (fg >= MAXSMOKEELEM) fg = (float) MAXSMOKEELEM - 0.5; for (i = 0; i < (int) fg; i ++) { float fac = (float) i / fg; smoke->setSmoke (tl->x - sx - forcex * fac, tl->y - sy - forcey * fac, tl->z - sz - forcez * fac, (int) phi, 39 - i); } /* smoke->setSmoke (tl->x - sx - forcex * 0.6, tl->y - sy - forcey * 0.6, tl->z - sz - forcez * 0.6, (int) phi, 36); smoke->setSmoke (tl->x - sx - forcex * 0.4, tl->y - sy - forcey * 0.4, tl->z - sz - forcez * 0.4, (int) phi, 37); smoke->setSmoke (tl->x - sx - forcex * 0.2, tl->y - sy - forcey * 0.2, tl->z - sz - forcez * 0.2, (int) phi, 38); smoke->setSmoke (tl->x - sx, tl->y - sy, tl->z - sz, (int) phi, 39); smoke->move (dt, 5);*/ smoke->move (dt, (int) fg + 1); smokettl += timestep; } if (!active) // not active, then exit { return; } if (explode > 0 || sink > 0) // exploding or sinking, then exit { thrust = 0; return; } // do expensive calculations only once float myheight = l->getExactHeight (tl->x, tl->z); float targetheight = tl->y; if (target != NULL) targetheight = l->getExactHeight (target->tl->x, target->tl->z); if (target != NULL) disttarget = distance (target); // distance to target else disttarget = 1; // get a new target if necessary if (id >= MISSILE1 && id <= MISSILE2) { if (target == NULL) ttl = 0; else if (!target->active) ttl = 0; } if (target == NULL) { if (bomber) targetNearestGroundEnemy (f); else targetNearestEnemy (f); } if (target != NULL) if (!target->active) { if (bomber) targetNearestGroundEnemy (f); else targetNearestEnemy (f); } if (id >= FIGHTER1 && id <= FIGHTER2) // for fighters do the following { if (haveMissile () && target != NULL) { float dgamma = atan ((target->tl->y - tl->y) / disttarget) * 180 / PI - (gamma - 180); float dphi = getAngle (target); if (missiletype == MISSILE_DF1 - MISSILE1) { ttf = 0; } else if (fabs (dphi) < 50 && fabs (dgamma) < 50 && party != target->party) { if (disttarget < 75) { if (ttf > 0) { if (missiletype >= 0 && missiletype <= 2) { if (target->id >= FIGHTER1 && target->id <= FIGHTER2) { float dphi = fabs (phi - target->phi); if (dphi > 270) dphi = 360 - dphi; if (dphi < 45) ttf -= 2 * dt; else ttf = 50 * timestep; } } else if (missiletype == 6 || missiletype == 7) { if (target->id >= FIGHTER1 && target->id <= FIGHTER2) { ttf -= 2 * dt; } } else { if (target->id > FIGHTER2) { ttf -= 2 * dt; } } } } } else { ttf = 50 * timestep; } } } if (!ai) return; /* // The following model would be the REAL AI behaviour! // However enemies are too weak! CVector3 targetvec; if (target != NULL) { targetvec.take (target->tl); float disttarget = distance (&targetvec); if (disttarget > 30) disttarget = 30; targetvec.x += target->forcex * disttarget / 5; targetvec.y += target->forcey * disttarget / 5; targetvec.z += target->forcez * disttarget / 5; } int lsdst = 5; float flyx1 = tl->x + forcex * lsdst, flyz1 = tl->z + forcez * lsdst; float flyx2 = tl->x + forcex * lsdst * 4, flyz2 = tl->z + forcez * lsdst * 4; float h1 = tl->y - l->getMaxHeight (flyx1, flyz1); float h2 = tl->y - l->getMaxHeight (flyx2, flyz2); if (h1 < 5 || h1 > 25) { targetvec.x = flyx1; targetvec.z = flyz1; targetvec.y = l->getMaxHeight (flyx1, flyz1) + 15; } disttarget = distance (&targetvec); float dgamma = getAngleH (&targetvec); float dphi = getAngle (&targetvec); if (fabs (dphi) < 1E-5) dphi = 1E-5; float delta = atan (dgamma / dphi) * 180 / PI; if (dphi > 0) delta -= 180; rectheta = -delta - 90; if (rectheta < -180) rectheta += 360; if (rectheta >= 180) rectheta -= 360; recelevatoreffect = 1; if (disttarget > 50 && fabs (dphi) < 25) { rectheta = 0; recelevatoreffect = 0; if (fabs (theta - rectheta) < 5) recrolleffect = 0; else if (theta - rectheta >= 5) recrolleffect = -1; else if (theta - rectheta <= -5) recrolleffect = 1; } else if (fabs (theta - rectheta) > 150 && fabs (dphi) < 30 && fabs (dgamma) < 30) { recrolleffect = 0; recelevatoreffect = -0.5; } else if (theta - rectheta > 50) { recrolleffect = -1; recelevatoreffect = 0; } else if (theta - rectheta < -50) { recrolleffect = 1; recelevatoreffect = 0; } else if (theta - rectheta > 20) { recrolleffect = -1; recelevatoreffect = 1; } else if (theta - rectheta < -20) { recrolleffect = 1; recelevatoreffect = 1; } else { recrolleffect = 0; recelevatoreffect = 1; } easymodel = 2; if (phi >= 360) phi -= 360; if (phi < 0) phi += 360; if (gamma >= 360) gamma -= 360; if (gamma < 0) gamma += 360; thrust = maxthrust * 0.7; float pulljoystick = 0.005; float nocorrection = 0.1; if (recrolleffect > rolleffect + nocorrection) rolleffect += pulljoystick * timestep; else if (recrolleffect < rolleffect - nocorrection) rolleffect -= pulljoystick * timestep; if (recelevatoreffect > elevatoreffect + nocorrection) elevatoreffect += pulljoystick * timestep; else if (recelevatoreffect < elevatoreffect - nocorrection) elevatoreffect -= pulljoystick * timestep; if (target) { if (disttarget < 20 && dphi < 5 && dgamma < 5) fireCannon (laser); } if (disttarget < 5 && dphi < 90 && dgamma < 90) { thrust = maxthrust / 2; } else { thrust = maxthrust; } return; */ // which height??? float recheight2; // this is the height, the object wants to achieve int lsdist = 15; float flyx = tl->x + forcex * lsdist, flyz = tl->z + forcez * lsdist; int flyxs = l->getCoord ((int) flyx), flyzs = l->getCoord ((int) flyz); { if (manoeverheight > 0) { // precalculated height recheight2 = l->getExactHeight (flyx, flyz) + recheight; } else { // missiles and non intelligent objects will not change their height due to the surface if ((id >= MISSILE1 && id <= MISSILE2 && target != NULL) || (tl->y - myheight > 8 && target != NULL && tl->y - myheight < 50/* && !manoeverheight*/)) { recheight2 = target->tl->y - 8 * target->thrust * SIN(target->gamma); } else { // precalculated height float flyx2 = tl->x + forcex * lsdist * 3, flyz2 = tl->z + forcez * lsdist * 3; float flyx3 = tl->x + forcex * lsdist * 8, flyz3 = tl->z + forcez * lsdist * 8; float h1 = l->getMaxHeight (flyx, flyz); float h2 = l->getMaxHeight (flyx2, flyz2); float h3 = l->getMaxHeight (flyx3, flyz3); h1 = h1 > h2 ? h1 : h2; h1 = h1 > h3 ? h1 : h3; recheight2 = recheight + h1; } } } // fire flares and chaff if (id >= FIGHTER1 && id <= FIGHTER2) // for fighters do the following { if (manoevertheta <= 0) for (i = 0; i < maxmissile; i ++) if (m [i]->ttl > 0) if (m [i]->target == this) { if (m [i]->id >= 0 && m [i]->id <= MISSILE_AIR3) { if ((easymodel == 1 && fabs (theta) >= 30) || (easymodel == 2 && fplayer->elevatoreffect >= 0.5)) { fireFlare (flare, m); fireflarettl += intelligence / 20 * timestep; } manoevertheta = 35 * timestep; } else { if ((easymodel == 1 && fabs (theta) >= 30) || (easymodel == 2 && fplayer->elevatoreffect >= 0.5)) { fireChaff (chaff, m); firechaffttl += intelligence / 20 * timestep; } manoevertheta = 35 * timestep; } } } // manoevers (may use the height information) if (manoeverstate && active && draw) { easymodel = 2; if (manoeverstate == 1) // Immelmann { recelevatoreffect = 0.05; if (fabs (theta) > 10) recrolleffect = -1; else { recrolleffect = 0; manoeverstate = 2; } } else if (manoeverstate == 2) { if (fabs (theta) <= 150) { recrolleffect = 0; recelevatoreffect = 1; } else { manoeverstate = 3; } } else if (manoeverstate == 3) { if (gamma < 170 || gamma > 190) { recrolleffect = 0; recelevatoreffect = 1; } else { manoeverstate = 4; } } else if (manoeverstate == 4) { if (fabs (theta) > 20) { recrolleffect = 1; recelevatoreffect = 0.05; } else { manoeverstate = 0; } } if (manoeverstate == 10) // climb vertical { recrolleffect = 0; recelevatoreffect = 1; if (gamma > 260 || gamma < 90) { recrolleffect = 0; recelevatoreffect = 0; manoeverstate = 11; } } else if (manoeverstate == 11) { if (fabs (tl->y - myheight) > 3) { manoeverstate = 12; } } else if (manoeverstate == 12) { recelevatoreffect = -0.5; if (gamma > 170 && gamma < 190) { recelevatoreffect = 0; manoeverstate = 0; } } if (manoeverstate == 20) // Roll { recelevatoreffect = 0.55; recrolleffect = 1; if (theta > 80 && theta < 90) { manoeverstate = 21; } } else if (manoeverstate == 21) { if (theta > -10 && theta < 10) { manoeverstate = 0; } } float pulljoystick = 0.005; float nocorrection = 0.1; if (recrolleffect > rolleffect + nocorrection) rolleffect += pulljoystick * timestep; else if (recrolleffect < rolleffect - nocorrection) rolleffect -= pulljoystick * timestep; if (recelevatoreffect > elevatoreffect + nocorrection) elevatoreffect += pulljoystick * timestep; else if (recelevatoreffect < elevatoreffect - nocorrection) elevatoreffect -= pulljoystick * timestep; return; } else { if (ai) easymodel = 1; } // calculate the recommended height, recheight2 depends on it if (manoeverheight > 0) manoeverheight -= dt; if (manoeverheight <= 0) { if (!(id >= FIGHTER1 && id <= FIGHTER2) && target != NULL) // no fighter, has target (missile, mine) { recheight = target->tl->y - targetheight; } else if (id == FIGHTER_TRANSPORT || id == FIGHTER_TRANSPORT2) // transporters have to stay higher { recheight = 40; manoeverheight = 1; } else if (id >= FIGHTER1 && id <= FIGHTER2 && target != NULL) // fighter, has target { if (target->id >= FIGHTER1 && target->id <= FIGHTER2) recheight = target->tl->y - targetheight; // target is a fighter else recheight = target->tl->y - targetheight + 5; // target is no fighter if (!l->isWater (l->f [flyxs] [flyzs])) // not flying above water { if (recheight < 3.5 + 0.01 * aggressivity) recheight = 3.5 + 0.01 * aggressivity; } float minh = 5.5 + 0.01 * aggressivity; // minimum height if (l->type == LAND_CANYON) minh = 6.5 + 0.01 * aggressivity; // stay higher in canyons if (fabs (tl->y - myheight) < minh) { recheight = 9 + 0.015 * aggressivity; if (fabs (tl->y - myheight) < minh * 0.3) { manoeverstate = 10; display ("Manoever: Vertical climb", LOG_ALL); } else { manoeverheight = 5 * timestep; // fly manoever to gain height } } if (disttarget < 50 && fabs (tl->y - myheight) > 25) { recheight = 8 + 0.025 * aggressivity; manoeverheight = 12 * timestep; } } } if (ttl <= 0 && id >= MISSILE1 && id <= MISSILE2 && id != MISSILE_MINE1) { recheight = -100; recheight2 = -100; recgamma = 90; } else if (ai) { if (target != NULL && ((id >= MISSILE1 && id <= MISSILE2) || (id >= FIGHTER1 && id <= FIGHTER2 && manoeverheight <= 0))) // is AGM { float dgamma = 0; if (disttarget <= -0.00001 || disttarget >= 0.00001) // no division by zero dgamma = atan ((target->tl->y - tl->y) / disttarget) * 180 / PI - (gamma - 180); recgamma = gamma + dgamma; // get recommended elevation to target } else { recgamma = (int) ((recheight2 - tl->y) * 10 - gamma + 360); } } // do a smooth roll float deltatheta; if (easymodel == 1) { deltatheta = rectheta - theta; if (fabs (dtheta) > 30) { dtheta = 0; } float mynimbility = fabs (deltatheta) / 5.0F * nimbility; if (mynimbility > nimbility) mynimbility = nimbility; float nimbility2 = mynimbility; if (nimbility2 >= -0.00001 && nimbility2 <= 0.00001) nimbility2 = 0.00001; if (deltatheta > 0 && dtheta < 0) dtheta += mynimbility * timefac; else if (deltatheta < 0 && dtheta > 0) dtheta -= mynimbility * timefac; else if (deltatheta > 0) { float estimatedtheta = dtheta * (dtheta + nimbility2 * 5 / timefac) / 2 / nimbility2; if (deltatheta > estimatedtheta) dtheta += mynimbility * timefac; else if (deltatheta < estimatedtheta) dtheta -= mynimbility * timefac; } else { float estimatedtheta = -dtheta * (dtheta - nimbility2 * 5 / timefac) / 2 / nimbility2; if (deltatheta < estimatedtheta) dtheta -= mynimbility * timefac; else if (deltatheta > estimatedtheta) dtheta += mynimbility * timefac; } if (dtheta > (nimbility * (1.0 + realspeed)) * timefac * 5.0F) dtheta = (nimbility * (1.0 + realspeed)) * timefac * 5.0F; theta += dtheta; // height changes if (easymodel == 1) { float nimbility1 = nimbility / 5; if (nimbility1 >= -0.00001 && nimbility1 <= 0.00001) nimbility1 = 0.00001; if (theta > maxtheta) theta = maxtheta; // restrict roll angle else if (theta < -maxtheta) theta = -maxtheta; float deltagamma = recgamma - gamma; if (deltagamma > 0 && dgamma < 0) dgamma += nimbility1 * timefac; else if (deltagamma < 0 && dgamma > 0) dgamma -= nimbility1 * timefac; else if (deltagamma > 0) { float estimatedgamma = dgamma * (dgamma + nimbility1 * 2) / nimbility1; if (id == 200) id = id; if (deltagamma > estimatedgamma + 2) dgamma += nimbility1 * timefac; else if (deltagamma < estimatedgamma - 2) dgamma -= nimbility1 * timefac; } else if (deltagamma < 0) { float estimatedgamma = -dgamma * (dgamma + nimbility1 * 2) / nimbility1; if (id == 200) id = id; if (deltagamma < estimatedgamma - 2) dgamma -= nimbility1 * timefac; else if (deltagamma > estimatedgamma + 2) dgamma += nimbility1 * timefac; } if (dgamma > manoeverability * (3.33 + 15.0 * realspeed) * timefac) dgamma = manoeverability * (3.33 + 15.0 * realspeed) * timefac; gamma += dgamma; } } if (gamma > 180 + maxgamma) gamma = 180 + maxgamma; else if (gamma < 180 - maxgamma) gamma = 180 - maxgamma; if (id >= MISSILE1 && id <= MISSILE2) { if (target == NULL) { ttl = 0; return; } else if (target->active == false) { ttl = 0; return; } } if (target == NULL) return; // fighter's targeting mechanism for missiles if (id >= FIGHTER1 && id <= FIGHTER2) // for fighters do the following { if (ai) { if (target->id >= FIGHTER1 && target->id <= FIGHTER2) { if (!selectMissileAirFF (m)) selectMissileAir (m); } else { selectMissileGround (m); } } } if (!ai || target == NULL) // no AI (player) or no target found, then exit { return; } int firerate; if (difficulty == 0) firerate = 12; else if (difficulty == 1) firerate = 6; else firerate = 3; float dx2, dz2, ex, ez; float dx = target->tl->x - tl->x, dz = target->tl->z - tl->z; // current distances if ((id >= FIGHTER1 && id <= FIGHTER2) || (id >= MISSILE1 && id <= MISSILE2) || (id >= FLAK1 && id <= FLAK2) || (id >= TANK1 && id <= TANK2)) { float t = 10.0 * disttarget; // generous time to new position if (t > 60) t = 60; // higher values will not make sense t *= (float) (400 - precision) / 400; int tt = (int) target->theta; if (tt < 0) tt += 360; float newphi = t * SIN(tt) * 5.0 * target->manoeverability; // new angle of target after time t if (newphi > 90) newphi = 90; else if (newphi < -90) newphi = -90; newphi += (float) target->phi; if (newphi >= 360) newphi -= 360; if (newphi < 0) newphi += 360; if ((id >= FIGHTER1 && id <= FIGHTER2) || (id >= FLAK1 && id <= FLAK2) || (id >= TANK1 && id <= TANK2)) { ex = target->tl->x - SIN(newphi) * t * target->realspeed * 0.25; // estimated target position x ez = target->tl->z - COS(newphi) * t * target->realspeed * 0.25; // estimated target position z } else { ex = target->tl->x - SIN(newphi) * t * target->realspeed * 0.05; // estimated target position x ez = target->tl->z - COS(newphi) * t * target->realspeed * 0.05; // estimated target position z } dx2 = ex - tl->x; dz2 = ez - tl->z; // estimated distances } else { dx2 = dx; dz2 = dz; } float a, w = phi; if (dz2 > -0.0001 && dz2 < 0.0001) dz2 = 0.0001; // get heading to target a = atan (dx2 / dz2) * 180 / PI; if (dz2 > 0) { if (dx2 > 0) a -= 180.0F; else a += 180.0F; } // this->aw = a; aw = a - w; // aw=0: target in front, aw=+/-180: target at back if (aw < -180) aw += 360; if (aw > 180) aw -= 360; if (manoevertheta > 0) manoevertheta -= dt; if (manoeverthrust > 0) manoeverthrust -= dt; // heading calculations if (id >= FIGHTER1 && id < FIGHTER_TRANSPORT) // for fighters do the following { if (!acttype && disttarget <= 1000 && manoevertheta <= 0) // no special action, near distance, no roll manoever { if (aw > 0) // positive angle { if (aw > 140 && disttarget > 50) { manoeverstate = 1; display ("Manoever: Immelmann", LOG_ALL); } else if (aw > 160.0F + 0.05 * intelligence && disttarget < 4 + 0.01 * intelligence) // target very near at the back { manoeverstate = 1; display ("Manoever: Immelmann", LOG_ALL); } else if (aw > 160 && disttarget < 25) // target is at the back { if (fabs (tl->y - myheight) > 7 && gamma >= 175 + intelligence / 100) // high enough over ground { manoeverstate = 20; // roll display ("Manoever: Roll", LOG_ALL); } else { rectheta = -90; if (manoevertheta <= 0) { manoevertheta = timestep * (100 + myrandom ((400 - intelligence) / 8)); // turn hard left or right display ("Manoever: Turn", LOG_ALL); } if (manoeverthrust <= 0) recthrust = maxthrust / (1.05F + (float) intelligence * 0.0015); // fly faster if (intelligence < 280 && manoeverheight <= 0) { recheight = 5; manoeverheight = timestep * (20 - intelligence / 50); display ("Manoever: Height change", LOG_ALL); } // stay low } } else if (aw < 40 && disttarget > 60) { rectheta = 0; } else if (aw < 20 && disttarget > 30) { rectheta = 0; } else // otherwise fly to target direction { int maw = aw > 90 ? 90 : (int) aw; int maw2 = 90 - maw; rectheta = 90 - maw2 * intelligence / 400; if (maw < 30) rectheta /= 2; if (maw < 5) { rectheta = 0; if (target->id >= FIGHTER1 && target->id <= FIGHTER2 && disttarget < 20) ((AIObj *) target)->manoevertheta = timestep * (50 - intelligence / 10); } } } else // same for negative angle { if (aw < -140 && disttarget > 50) { manoeverstate = 1; display ("Manoever: Immelmann", LOG_ALL); } else if (aw < -160.0F - 0.05 * intelligence && disttarget < 4 + 0.01 * intelligence) // target very near at the back { manoeverstate = 1; display ("Manoever: Immelmann", LOG_ALL); } else if (aw < -160 && disttarget < 25) { if (fabs (tl->y - myheight) > 7 && gamma >= 175 + intelligence / 100) // high enough over ground { manoeverstate = 20; // roll display ("Manoever: Roll", LOG_ALL); } else { rectheta = 90; if (manoevertheta <= 0) { manoevertheta = timestep * (100 + myrandom ((400 - intelligence) / 8)); display ("Manoever: Turn", LOG_ALL); } if (manoeverthrust <= 0) recthrust = maxthrust / (1.05F + (float) intelligence * 0.0015); if (intelligence < 280 && manoeverheight <= 0) { recheight = 5; manoeverheight = timestep * (20 - intelligence / 50); display ("Manoever: Height change", LOG_ALL); } } } else if (aw > -40 && disttarget > 60) { rectheta = 0; } else if (aw > -20 && disttarget > 30) { rectheta = 0; } else { int maw = aw < -90 ? -90 : (int) aw; int maw2 = -90 - maw; rectheta = -90 - maw2 * intelligence / 400; if (maw > -30) rectheta /= 2; if (maw > -5) { rectheta = 0; if (target->id >= FIGHTER1 && target->id <= FIGHTER2 && disttarget < 20) ((AIObj *) target)->manoevertheta = timestep * (50 - intelligence / 10); } } } } } else if (id >= MISSILE1 && id <= MISSILE2) // for missiles do the following { if (fabs (aw) < 50 && disttarget > 50) // target in front and minimum distance, then no roll rectheta = 0; else // otherwise chase target { if (aw < -90 || aw > 90) rectheta = 0; else if (aw > 0) { rectheta = aw > 90 ? 90 : aw; } else { rectheta = aw < -90 ? -90 : aw; } } } else if (id >= FLAK1 && id <= FLAK2) // ground-air-cannon { recthrust = 0; thrust = 0; if (aw > 5) { rectheta = maxtheta; } else if (aw < -5) { rectheta = -maxtheta; } else { rectheta = 0; } } else if (id >= TANK1 && id <= TANK2) // tanks { recthrust = maxthrust; thrust = maxthrust; // always at maximum thrust if (aw > 5) { rectheta = maxtheta; } else if (aw < -5) { rectheta = -maxtheta; } else { rectheta = 0; } if (firecannonttl <= 0) { if (id == TANK_AIR1) if (fabs (rectheta - theta) < 2 && fabs (aw) < 20 && disttarget < 40 && target->tl->y > tl->y + 2) { fireCannon (c); firecannonttl += firerate * timestep; } if (id == TANK_GROUND1) if (fabs (rectheta - theta) < 2 && fabs (aw) < 20 && disttarget < 35 && target->tl->y <= tl->y + 1 && target->tl->y >= tl->y - 1) { fireCannon (c); firecannonttl += firerate * timestep; } } } // thrust and manoever calculations if (id >= FIGHTER1 && id <= FIGHTER2) // fighters { if (disttarget > 5 + aggressivity / 12) // 2.5 seems to be best, but fighters become far too strong { if (disttarget < 50 && fabs (aw) > 30 && manoeverthrust <= 0) recthrust = maxthrust / (1.0F + (float) intelligence * 0.0025); else thrustUp (); // otherwise fly faster } else if (manoeverthrust <= 0) { if (recthrust > target->thrust) // adopt thrust of target { thrustDown (); } else { thrustUp (); } } if (disttarget > 50 && fabs (aw) < 20) // high distance and target in front, then fly straight rectheta = 0; if (manoeverthrust <= 0) if (disttarget < 25 && fabs (aw) > 160 && target->id >= TANK1) // avoid collisions { manoeverthrust = 25 * timestep; recthrust = maxthrust; if (difficulty == 0) recthrust = maxthrust * 0.8F; manoevertheta = 25 * timestep; rectheta = 0; manoeverheight = 25 * timestep; recheight = 10; } // fire cannon? float agr = 4.0 - (float) aggressivity / 100; if (firecannonttl <= 0) { if (fabs (rectheta - theta) < agr && fabs (aw) < 20 + agr * 4 && disttarget < 30) fireCannon (c); else if (disttarget < 2 + agr && fabs (aw) < 20 + agr * 4) fireCannon (c); } // fire missile? if (firemissilettl <= 0) { if (target->id >= FIGHTER1 && target->id <= FIGHTER2) { int z1 = 0; if (disttarget < 15 && fabs (aw) < 20) { for (i = 0; i < maxfighter; i ++) { if (target == f [i]->target && party == f [i]->party) z1 ++; } if (z1 >= 3) { manoevertheta = 15 * timestep; firemissilettl = 10 * timestep; if (myrandom (2)) rectheta = 90; else rectheta = -90; targetNextEnemy (f); } } if (firemissilettl <= 0) { if (fabs (rectheta - theta) < agr / 2 && fabs (aw) < agr && disttarget < 45) { fireMissile (m, (AIObj *) target); firemissilettl += aggressivity * timestep; } } } else // ground target { if (fabs (rectheta - theta) < 5 + agr * 4 && fabs (aw) < 5 + agr * 4 && disttarget < 50) if (!(l->lsticker & 7)) { fireMissileGround (m); firemissilettl += aggressivity / 2 * timestep; } } } } if ((id >= FLAK1 && id <= FLAK2) || id == SHIP_CRUISER || id == SHIP_DESTROYER1 || id == TANK_TRSAM1) { if (firecannonttl <= 0) for (int i = 0; i < maxfighter; i ++) if (f [i]->active) if (party != f [i]->party) { disttarget = distance (f [i]); // distance to target ex = f [i]->tl->x; // estimated target position x ez = f [i]->tl->z; // estimated target position z dx2 = ex - tl->x; dz2 = ez - tl->z; // estimated distances w = (int) phi; if (dz2 > -0.0001 && dz2 < 0.0001) dz2 = 0.0001; a = (atan (dx2 / dz2) * 180 / PI); if (dz2 > 0) { if (dx2 > 0) a -= 180; else a += 180; } aw = a - w; if (aw < -180) aw += 360; if (aw > 180) aw -= 360; if (id == FLAK_AIR1) if (f [i]->tl->y > tl->y + 2) { if (fabs (aw) <= 20 && disttarget < 50) // + aggressive fireCannon (c, phi + aw); firecannonttl = firerate * timestep; } if (id == SHIP_DESTROYER1) if (f [i]->tl->y > tl->y + 2) { if (aw >= 0 && aw < 40 && disttarget < 50) // + aggressive fireCannon (c, phi + aw); if (aw >= -40 && aw < 0 && disttarget < 50) // + aggressive fireCannon (c, phi + aw); if (aw >= 120 && aw < 160 && disttarget < 50) // + aggressive fireCannon (c, phi + aw); if (aw >= -160 && aw < -120 && disttarget < 50) // + aggressive fireCannon (c, phi + aw); firecannonttl = firerate * timestep; } if (firemissilettl <= 0) if (id == FLARAK_AIR1) if (fabs (aw) < 25 && disttarget < 45) // + aggressive if (f [i]->tl->y > tl->y + 2) { ttf = 0; fireMissileAirFF (m, f [i]); firemissilettl += (20 + firerate * 10) * timestep; } if (id == TANK_TRSAM1) { if (firemissilettl <= 0) if (aw >= -30 && aw < 30 && disttarget < 60) // + aggressive { ttf = 0; fireMissileAirFF (m, f [i]); firemissilettl += aggressivity / 5 * timestep; missiles [6] ++; // unlimited ammo } } if (id == SHIP_CRUISER) { if (firemissilettl <= 0) if (aw >= -30 && aw < 30 && disttarget < 60) // + aggressive { ttf = 0; fireMissileAirFF (m, f [i]); firemissilettl += aggressivity / 5 * timestep; missiles [6] ++; // unlimited ammo } if (firecannonttl <= 0) { fireCannon (c, phi + aw); } } } } if (id >= FIGHTER1 && id <= FIGHTER2) { if (rectheta > 90 - precision / 5) rectheta = 90 - precision / 5; else if (rectheta < -90 + precision / 5) rectheta = -90 + precision / 5; } } #endif