123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694 |
- #include "pch.h"
- // Utility functions:
- /*-------------------------------------------------------------------------
- * Function: getDirection
- *-------------------------------------------------------------------------
- * Purpose:
- * Get the button control mask to thrust in a given direction
- */
- int getDirection(const Vector& dP,
- const Orientation& orientation)
- {
- float z = dP * orientation.GetForward();
- float y = dP * orientation.GetUp();
- float x = dP * orientation.GetRight();
- double absX = fabs(x);
- double absY = fabs(y);
- double absZ = fabs(z);
- return (absX > absY)
- ? ((absX > absZ)
- ? (x >= 0.0f ? rightButtonIGC : leftButtonIGC)
- : (z >= 0.0f ? forwardButtonIGC : backwardButtonIGC))
- : ((absY > absZ)
- ? (y >= 0.0f ? upButtonIGC : downButtonIGC)
- : (z >= 0.0f ? forwardButtonIGC : backwardButtonIGC));
- }
- /*-------------------------------------------------------------------------
- * Enumeration: ShotStatus
- *-------------------------------------------------------------------------
- * Purpose:
- * How clean of a shot do we have??
- * Notes:
- * The values are ordered in order of importantance
- */
- enum ShotStatus
- {
- c_ssCleanShot = 0,
- c_ssNeutralBehind = 1,
- c_ssFriendlyBehind = 2,
- c_ssBlocked = 3
- };
- /*-------------------------------------------------------------------------
- * Function: checkModels
- *-------------------------------------------------------------------------
- * Purpose:
- * Check the model list to find anything affecting the status of the shot
- * that we would like to fire
- */
- ShotStatus checkModels(IshipIGC* pShip, ImodelIGC* pTarget, const ModelListIGC* models, const Vector& direction, const float dist2, const float range2)
- {
- assert(models);
- ShotStatus ss = c_ssCleanShot;
- IsideIGC* mySide = pShip->GetSide();
- // Traverse the object list
- for (ModelLinkIGC* mLink = models->first(); mLink != NULL; mLink = mLink->next())
- {
- ImodelIGC* m = mLink->data();
- IsideIGC* side = m->GetSide();
- if ((m != pTarget) && (m != pShip) && ((side == NULL) || (side == mySide))) // Then it's something that I don't want to hit
- {
- // Is it in my line of fire?
- Vector dp = m->GetPosition() - pShip->GetPosition();
- float dot = dp * direction;
- float dp2 = dp.LengthSquared();
- // Is it in front of me and in my weapons range??
- if (dot >= 0.0f && dp2 < range2)
- {
- assert((direction.LengthSquared() < 1.05f) && (direction.LengthSquared() > 0.95));
- Vector closest = pShip->GetPosition() + direction * dot;
- Vector offset = closest - m->GetPosition();
- float offsetLength2 = offset.LengthSquared();
- float r = m->GetRadius();
- if (offsetLength2 < (r * r))
- {
- if (dp2 > dist2)
- ss = (side == NULL) ? c_ssNeutralBehind : c_ssFriendlyBehind;
- else {
- return c_ssBlocked;
- }
- }
- }
- }
- }
- return ss;
- }
- /*-------------------------------------------------------------------------
- * Function: getShotStatus
- *-------------------------------------------------------------------------
- * Purpose:
- * Send the appropriate model lists into checkmodels to get the status of the
- * shot that we would like to fire
- */
- ShotStatus getShotStatus(IshipIGC* pShip, ImodelIGC* pTarget, const Vector& direction, float range)
- {
- Vector vShipToTarget = pTarget->GetPosition() - pShip->GetPosition();
- float dist2 = vShipToTarget.LengthSquared();
- float flRange2 = range * range;
- ShotStatus sStatus = c_ssCleanShot;
- IclusterIGC* cluster = pTarget->GetCluster();
- if (cluster)
- {
- // Try to check in order of likeliness (and size of the list)
-
- // Check the planets
- ShotStatus ss = checkModels(pShip, pTarget, reinterpret_cast<const ModelListIGC*>(cluster->GetAsteroids()), direction, dist2, flRange2);
- if (ss == c_ssBlocked) return c_ssBlocked;
- if (ss > sStatus) sStatus = ss;
- // Check the stations
- ss = checkModels(pShip, pTarget, reinterpret_cast<const ModelListIGC*>(cluster->GetStations()), direction, dist2, flRange2);
- if (ss == c_ssBlocked) return c_ssBlocked;
- if (ss > sStatus) sStatus = ss;
- // Check the warps
- ss = checkModels(pShip, pTarget, reinterpret_cast<const ModelListIGC*>(cluster->GetWarps()), direction, dist2, flRange2);
- if (ss == c_ssBlocked) return c_ssBlocked;
- if (ss > sStatus) sStatus = ss;
- // Check the ships
- ss = checkModels(pShip, pTarget, reinterpret_cast<const ModelListIGC*>(cluster->GetShips()), direction, dist2, flRange2);
- if (ss > sStatus) sStatus = ss;
- }
- return sStatus;
- }
- // External functions:
- /*-------------------------------------------------------------------------
- * Function: StopEverything
- *-------------------------------------------------------------------------
- * Purpose:
- * Clear the ship controls
- */
- bool StopEverything(IshipIGC* pShip, Time lastUpdate)
- {
- assert(pShip);
- pShip->SetStateBits(lastUpdate, weaponsMaskIGC | buttonsMaskIGC, 0); // stop all firing
- ControlData controls;
- controls.Reset();
- pShip->SetControls(controls); // zero the motion bits
- return true;
- }
- /*-------------------------------------------------------------------------
- * Function: StrafeAttackTarget
- *-------------------------------------------------------------------------
- * Purpose:
- * To fly straight at the target, guns blazing, and then pull up at the
- * last second.
- * Notes:
- * This never quite panned out. You can look at bug 1191 for information.
- * Basically, using the goto function and trying to create a nice path
- * for the drones to come in and then pull up was next to impossible.
- * They would either get too much momentum towards their target, and then
- * not be able to pull up (especially for stations). First, I was hitting
- * this in the DoGotoAction:
- * if ((offsetLength2 < (r * r)) && (offsetLength2 > 0.1f))
- *
- * In otherwords, since we couldn't divide by zero, if we were perfectly
- * on path with an obstacle, we would just hit it. That meant that I could
- * not just GOTO our target and let the dodge code do it's thing. So I
- * played with making a nice arc in at the target, and then up. Once again,
- * I either got a lot of good shots in, and then couldn't pull up, or I
- * would only get one or two shots in if I was pulling up in enough time.
- * I think doing it right would mean NOT using the DoGotoAction function,
- * and that was more than I felt I should do in the last week. Too dangerous.
- */
- /*
- bool StrafeAttackTarget(IshipIGC* pShip,
- ImodelIGC* pTarget,
- Time lastUpdate,
- float dt,
- float shootSkill,
- float moveSkill,
- float howClose)
- {
- int buttonsM = 0;
- Vector direction = pTarget->GetPosition() - pShip->GetPosition();
- IweaponIGC* w = (IweaponIGC*)pShip->GetMountedPart(ET_Weapon, 0);
- if (w && !OBJECT_IS_BAD(pTarget))
- {
- IprojectileTypeIGC* pt = w->GetProjectileType();
- float lifespan = pt->GetLifespan();
- Vector direction;
- Vector myPosition = pShip->GetPosition();
- assert (pt);
- float tToHit = solveForLead(pShip, pTarget, w, &direction, shootSkill);
-
- Vector destination = (tToHit < lifespan)
- ? (direction + pTarget->GetPosition())
- : pTarget->GetPosition();
- destination += (pShip->GetOrientation().GetUp() * pShip->GetRadius() * 5.0f) / ((destination - myPosition).Length());
-
- // destination += (CrossProduct(pShip->GetOrientation().GetRight(), destination).Normalize() * pShip->GetRadius() * 5.0f) / ((destination - myPosition).Length());
- Vector destVel = (destination - myPosition).Normalize() * pShip->GetEffectiveTopSpeed();
- // go straight for the ship, let the dodge code take care of any problems.
- DoGotoAction(pShip,
- lastUpdate,
- NULL,
- destination,
- // destVel,
- Vector::GetZero(),
- 0.0f,
- dt,
- moveSkill, false, false, false);
- ControlData controls;
- controls.Reset();
- //just need the angle... todo: replace this with only the necessary calcs, then delete all controls in this func.
- float deltaAngle = turnToFace((tToHit < lifespan) ? direction : (pTarget->GetPosition() - pShip->GetPosition()),
- dt, pShip, &controls, moveSkill);
- //shoot when appropriate
- if ((tToHit <= lifespan) && (deltaAngle < pi / 48.0f))
- buttonsM = allWeaponsIGC;
- if (buttonsM) {
- // if I am about to hit anything I don't want to hit,
- // then return false
-
- ShotStatus status = getShotStatus(pShip, pTarget, (tToHit < lifespan) ? direction : (pTarget->GetPosition() - pShip->GetPosition()), lifespan * pt->GetSpeed());
- if ((status == c_ssBlocked) ||
- (status == c_ssFriendlyBehind && (tToHit > shootSkill + 0.1))) // there is something behind him, do I risk it?
- {
- // not going to try it
- buttonsM = 0;
- }
- }
- }
- pShip->SetStateBits(lastUpdate, weaponsMaskIGC | buttonsMaskIGC, buttonsM);
- // ship controls will be set in the DoGotoAction function
- return (buttonsM != 0) ? true : false;
- }
- */
- /*-------------------------------------------------------------------------
- * Function: StationaryAttackTarget
- *-------------------------------------------------------------------------
- * Purpose:
- * Don't thrust at all, just rotate your target into position and fire away
- */
- bool StationaryAttackTarget(IshipIGC* pShip,
- Time lastUpdate,
- ImodelIGC* pTarget,
- float dt,
- float shootSkill,
- float moveSkill,
- bool fCareful) // fCareful: make sure you don't hit friendlies
- {
- ControlData controls;
- controls.Reset();
- int buttonsM = 0;
- assert(!OBJECT_IS_BAD(pShip));
- // Make sure I have a weapon
- IweaponIGC* w = (IweaponIGC*)pShip->GetMountedPart(ET_Weapon, 0);
- if (w && !OBJECT_IS_BAD(pTarget))
- {
- IprojectileTypeIGC* pt = w->GetProjectileType();
- assert (pt);
- float lifespan = pt->GetLifespan();
- Vector direction;
- float tToHit = solveForLead(pShip, pTarget, w, &direction, shootSkill);
- // Pivot to shoot the enemy
- float deltaAngle = turnToFace((tToHit < lifespan) ? direction : (pTarget->GetPosition() - pShip->GetPosition()),
- dt, pShip, &controls, moveSkill);
- // Shoot when appropriate
- if ((tToHit <= lifespan) && (deltaAngle < pi / 32.0f))
- buttonsM = allWeaponsIGC;
- if (fCareful && buttonsM) {
- // If I am about to hit anything I don't want to hit, then don't fire
-
- ShotStatus status = getShotStatus(pShip, pTarget, direction, lifespan * pt->GetSpeed());
- if ((status == c_ssBlocked) ||
- (status == c_ssFriendlyBehind && (tToHit > shootSkill + 0.1))) // There is something behind him, do I risk it?
- {
- // Not going to try it
- buttonsM = 0;
- }
- }
- }
- pShip->SetStateBits(lastUpdate, weaponsMaskIGC | buttonsMaskIGC, buttonsM);
- pShip->SetControls(controls);
- return (buttonsM != 0) ? true : false;
- }
-
- /*-------------------------------------------------------------------------
- * Function: DoGotoAction
- *-------------------------------------------------------------------------
- * Purpose:
- * This is the most used function in all of the drone code. It takes the
- * ship, the target position, and a slew of other parameters, and does three
- * things:
- * 1) Dodges immediate impacts
- * 2) Evaluates a path around huge static obstacles
- * 3) Pulls into the desired radius of the target position, or orbits
- */
- bool DoGotoAction(IshipIGC* pShip,
- Time lastUpdate,
- ImodelIGC* pTarget,
- Vector position,
- Vector velocity,
- float radius,
- float dt,
- float skill,
- bool bThroughWarps,
- bool orbit,
- bool dodgeBullets)
- {
- if (OBJECT_IS_BAD(pShip))
- return false;
- if (pTarget && pTarget->GetCluster() != pShip->GetCluster()) // Make sure that we are in the right cluster
- {
- if (!bThroughWarps)
- {
- StopEverything(pShip, lastUpdate);
- return false;
- }
- IwarpIGC* gotoWarp = FindPath(pShip, pTarget);
- pTarget = (ImodelIGC*) gotoWarp;
- position = gotoWarp->GetPosition();
- velocity = Vector::GetZero();
- radius = 0.0f;
- }
- const Vector& myPosition = pShip->GetPosition();
- const Vector& myVelocity = pShip->GetVelocity();
- const Orientation& myOrientation = pShip->GetOrientation();
- float myRadius = pShip->GetRadius();
- bool fCheckTargetForCollision;
- if (pTarget)
- {
- //The only time we do not check for a collision vs. the target is when it is a warp we want
- //to go through
- switch (pTarget->GetObjectType())
- {
- case OT_warp:
- {
- fCheckTargetForCollision = !bThroughWarps;
- }
- break;
- case OT_station:
- {
- fCheckTargetForCollision = false;
- }
- break;
- default:
- fCheckTargetForCollision = true;
- }
- //However far we want to be from the target, make it the distance from our bounding sphere
- radius += myRadius;
- }
- else
- {
- //Sorta weird: no target implies we want to get to a position exactly, so worry about "hitting" the point
- fCheckTargetForCollision = true;
- }
- Vector path = position - myPosition;
- float distance2 = path.LengthSquared();
- float maxSpeed = pShip->GetHullType()->GetMaxSpeed();
- float acceleration = pShip->GetHullType()->GetThrust() / pShip->GetMass();
- float speed = myVelocity.Length();
- float timeToDodge = (speed / (acceleration * pShip->GetHullType()->GetBackMultiplier())); //Worst case time to stop
- float distOffOrbit = 0.0f;
- //This behavior as 3 parts: avoid hitting stuff, get close to my goal,
- //and approach my goal without hitting it.
- bool fDiverted = false;
- //See if we are about to hit something (anything) or if there is something blocking out path to the goal
- ImodelIGC* modelCollide = NULL;
- float tCollide = (timeToDodge > 5.0f) ? timeToDodge : 5.0f;
- Vector directionCollide;
- {
- IclusterIGC* cluster = pShip->GetCluster();
- assert (cluster);
- const ModelListIGC* models = cluster->GetModels();
- assert (models);
- // There are cases that we DO want to check the target for collisions, like when out destination is in
- // respect to the target, but not the center of the target, and when we are going to a warp but not through the warp
- for (ModelLinkIGC* l = models->first(); (l != NULL); l = l->next())
- {
- ImodelIGC* m = l->data();
- ObjectType type = m->GetObjectType();
- if (m != pShip && (m != pTarget || fCheckTargetForCollision))
- {
- // Also make sure that we never dodge our own projectiles:
- if (type == OT_projectile &&
- ( (((IprojectileIGC*)m)->GetLauncher() == pShip) || !dodgeBullets ))
- continue;
- if (type == OT_missile && ((ImissileIGC*)m)->GetLauncher() == pShip)
- continue;
- float r = m->GetRadius() + myRadius + 20.0f;
- Vector dp = m->GetPosition() - myPosition;
- float d2 = dp.LengthSquared();
- float t = (d2 > r * r)
- ? solveForImpact(dp,
- m->GetVelocity()*skill - myVelocity,
- 0.0f,
- r,
- &directionCollide)
- : 0.0f;
- assert (t >= 0.0f);
- // Add some adjustments for skill level
- float adj = ((type == OT_projectile) || (type == OT_missile)) ? skill : 1.0f;
- if (t < tCollide*adj)
- {
- modelCollide = m;
- tCollide = t;
- }
- if ((type == OT_asteroid) || (type == OT_station) || (type == OT_ship) || (type == OT_warp))
- {
- if (orbit)
- { // Do we really want to do avoid this thing????
- // Don't do this unless we are so far away from our target that we are basically moving right towards it.
- }
- else
- {
- // A big non-moving thing ... is it in the way?
- float dot = dp * path;
- if ((dot >= 0.0f) && (d2 < distance2))
- {
- // Well ... it's not behind us (looking the way we want to go), and it is closer than where we'd like to go.
- Vector closest = myPosition + path * (dot / distance2);
- Vector offset = closest - m->GetPosition();
- float offsetLength2 = offset.LengthSquared();
- if ((offsetLength2 < (r * r)) && (offsetLength2 > 0.1f))
- {
- position = m->GetPosition() + offset * (r / (float)sqrt(offsetLength2));
- velocity = Vector::GetZero();
- path = position - myPosition;
- distance2 = path.LengthSquared();
- radius = 0.0f; //We want to go to this point exactly.
- orbit = false;
- fDiverted = true;
- }
- }
- }
- }
- }
- }
- }
- bool fAreWeThereYet = false;
- ControlData controls;
- controls.jsValues[c_axisYaw] = controls.jsValues[c_axisPitch] = controls.jsValues[c_axisRoll] = 0.0f;
- int stateM = 0;
- if (modelCollide)
- {
- // We are going to collide: find our relative positions at the time of collision
- // and generate as much of an away vector as possible
- const Vector& hisVelocity = modelCollide->GetVelocity();
- Vector directionAway = myPosition - modelCollide->GetPosition() +
- tCollide * (myVelocity - hisVelocity);
- // Dodging along his velocity vector doesn't do much good, so ...
- float lhv2 = hisVelocity.LengthSquared();
- if (lhv2 > 1.0f) // but, if he's moving slow, it doesn't matter
- {
- float lda2 = directionAway.LengthSquared();
- assert (lda2 != 0.0f); // should be, at least, the sum of the radii
- // Remove the component of his velocity from directionAway
- float dp = hisVelocity * directionAway;
- if (dp * dp > 0.99f * lhv2 * lda2)
- {
- // 99% (or more) of the away vector is parallel to his velocity vector ...
- // pick an arbitrary axis that is not parallel with his velocity
- Vector arbitrary(1.0f, 0.0f, 0.0f);
- float dot = arbitrary * hisVelocity;
- if (dot * dot > 0.99f * lhv2)
- {
- // Try the y axis as an arbitrary axis
- arbitrary.x = 0.0f;
- arbitrary.y = 1.0f;
- }
- // The direction in which to dodge is perpendicular to both his velocity and the arbitrary vector
- directionAway = CrossProduct(arbitrary, hisVelocity);
- }
- else
- {
- // The direction vector has enough of a perpendicular component to the
- // velocity vector to make this worthwhile
- // Subtract out the component of directionAway that is parallel to hisVelocity
- directionAway -= hisVelocity * (dp / lhv2);
- }
- }
- // Thrust along the away vector
- controls.jsValues[c_axisThrottle] = 1.0f;
- stateM = getDirection(directionAway, myOrientation);
- }
- else
- {
- Vector desiredVelocity;
- if (orbit)
- {
- assert(path.LengthSquared() > 0.0f);
- // Make sure that our radius is acceptable
- if (pTarget && radius < pTarget->GetRadius() + pShip->GetRadius())
- radius = pTarget->GetRadius() + pShip->GetRadius();
- // Get the tangent vector
- desiredVelocity = CrossProduct(path, pShip->GetVelocity());
- if (desiredVelocity.LengthSquared() <= 0.01f)
- {
- desiredVelocity = CrossProduct(path, pShip->GetOrientation().GetForward());
- if (desiredVelocity.LengthSquared() <= 0.01f)
- desiredVelocity = pShip->GetOrientation().GetRight();
- else
- desiredVelocity = CrossProduct(desiredVelocity, path);
- }
- else
- desiredVelocity = CrossProduct(desiredVelocity, path);
- // Adjust the tangent vector to smoothly approach the orbit if we are not currently in the orbit
- float distance = (float)sqrt(distance2);
- distOffOrbit = distance - radius;
- float topOrbitSpeed = (distOffOrbit > radius)
- ? maxSpeed
- : ((distOffOrbit < 0)
- ? maxSpeed/2
- : (maxSpeed * distance / (2 * radius)));
- float vel2= velocity.LengthSquared();
- desiredVelocity.SetNormalize();
- if (vel2<= 0.0f || vel2> topOrbitSpeed*topOrbitSpeed)
- desiredVelocity *= topOrbitSpeed;
- else
- desiredVelocity *= ((float)sqrt(vel2));
- // Spiral towards the perfect orbit
- Vector spiral = path.Normalize() * distOffOrbit;
- desiredVelocity += spiral;
- }
- else
- {
- // We do not have to worry about hitting anything real soon, so concentrate on following our target
- Vector direction;
- float t = solveForImpact(path,
- velocity,
- maxSpeed,
- radius,
- &direction);
- // t is the time required to get to the desired distance from our target.
- // If we are inside out manuevering envelope, match the targets velocity vector
- // otherwise, blend in a combination of the shortest path and the velocity vector
- desiredVelocity = velocity;
- if ((t <= timeToDodge) && fCheckTargetForCollision && (!fDiverted))
- {
- //the distance traveled is proportional to the square of the elapsed time,
- //so pick a velocity based on the square of the time ratio
- //float f = t / timeToDodge;
- //desiredVelocity += direction * (speed * f * f);
- }
- else if (t <= 1.0e10f)
- desiredVelocity += direction * maxSpeed;
- else
- {
- // Were not able to find any impact solution,
- // so if we are too close (a reason not to find a solution, move apart)
- // if we are too far, move closer
- float distance = (float)sqrt(distance2);
- if (distance < radius)
- desiredVelocity -= path * (maxSpeed * (radius - distance) / (radius * distance));
- else if (distance > 2.0f * radius)
- desiredVelocity += path * (maxSpeed / distance);
- }
- }
- float ldv2 = desiredVelocity.LengthSquared();
- if (ldv2 > 1.0f)
- {
- float deltaAngle = turnToFace(desiredVelocity,
- dt, pShip, &controls, skill);
- if (deltaAngle < pi * 0.25f)
- {
- // We are facing along (or close to) our desired velocity vector, set the throttle
- // appropriately
- controls.jsValues[c_axisThrottle] = (ldv2 < maxSpeed * maxSpeed)
- ? 2.0f * ((float)sqrt(ldv2) / maxSpeed - 0.5f)
- : 1.0f;
- }
- else
- {
- // We are not facing along (or close to) our desired velocity vector,
- // use side thrust to get there
- controls.jsValues[c_axisThrottle] = 1.0f; //use all of the throttle
- stateM = getDirection(desiredVelocity - myVelocity, myOrientation);
- }
- }
- else
- {
- controls.jsValues[c_axisThrottle] = -1.0f;
- fAreWeThereYet = !fDiverted;
- }
- }
- pShip->SetStateBits(lastUpdate, buttonsMaskIGC | weaponsMaskIGC, stateM); // Force all weapon to non-shooting
- pShip->SetControls(controls);
- if (orbit)
- return (distOffOrbit > -5.0f && distOffOrbit < 5.0f);
-
- return fAreWeThereYet;
- }
|