From 93c36ce61c3ce72341ca6f883a29ce2d6c9472c6 Mon Sep 17 00:00:00 2001 From: tibetiroka <68112292+tibetiroka@users.noreply.github.com> Date: Tue, 19 Mar 2024 00:15:20 +0100 Subject: [PATCH] feat(mechanics): Disabled fighters and drones will no longer be hit by stray projectiles (#9760) This greatly increases their odds of survival after being disabled. Explosions will still damage them. --- source/AI.cpp | 3 ++- source/CollisionSet.cpp | 58 ++++++++--------------------------------- source/CollisionSet.h | 11 ++++---- source/Engine.cpp | 5 ++++ source/Projectile.cpp | 12 ++++++--- source/Projectile.h | 1 + 6 files changed, 33 insertions(+), 57 deletions(-) diff --git a/source/AI.cpp b/source/AI.cpp index 1ddb766ebbec..124646cb135e 100644 --- a/source/AI.cpp +++ b/source/AI.cpp @@ -675,7 +675,8 @@ void AI::Step(Command &activeCommands) // Each ship only switches targets twice a second, so that it can // focus on damaging one particular ship. targetTurn = (targetTurn + 1) & 31; - if(targetTurn == step || !target || target->IsDestroyed() || (target->IsDisabled() && personality.Disables()) + if(targetTurn == step || !target || target->IsDestroyed() || (target->IsDisabled() && + (personality.Disables() || (target->CanBeCarried() && !personality.IsVindictive()))) || (target->IsFleeing() && personality.IsMerciful()) || !target->IsTargetable()) { target = FindTarget(*it); diff --git a/source/CollisionSet.cpp b/source/CollisionSet.cpp index 3a4ba8f0374c..f573d2d5b7d9 100644 --- a/source/CollisionSet.cpp +++ b/source/CollisionSet.cpp @@ -39,34 +39,6 @@ namespace { constexpr int USED_MAX_VELOCITY = MAX_VELOCITY - 1; // Warn the user only once about too-large projectile velocities. bool warned = false; - - - // Keep track of the closest collision found so far. If an external "closest - // hit" value was given, there is no need to check collisions farther out - // than that. - class Closest { - public: - explicit Closest(double closestHit) - : closest_dist(closestHit) - , closest_body(nullptr) - {} - - void TryNearer(double new_closest, Body *new_body) - { - if(new_closest >= closest_dist) - return; - - closest_dist = new_closest; - closest_body = new_body; - } - - double GetClosestDistance() const { return closest_dist; } - Body *GetClosestBody() const { return closest_body; } - - private: - double closest_dist; - Body *closest_body; - }; } @@ -166,9 +138,8 @@ void CollisionSet::Finish() -// Get all collisions for the given projectile. Collisions are not necessarily -// sorted by distance. If the projectile is incapable if impacting multiple ships -// in the same frame then only the closest collision is returned. +// Get all possible collisions for the given projectile. Collisions are not necessarily +// sorted by distance. const vector &CollisionSet::Line(const Projectile &projectile) const { // What objects the projectile hits depends on its government. @@ -177,15 +148,15 @@ const vector &CollisionSet::Line(const Projectile &projectile) const // Convert the projectile to a line represented by its start and end points. Point from = projectile.Position(); Point to = from + projectile.Velocity(); - return Line(from, to, pGov, projectile.Target(), projectile.HitsRemaining() != 1); + return Line(from, to, pGov, projectile.Target()); } -// Get all collisions along a line. Collisions are not necessarily sorted by -// distance. If the all variable is false then only the closest collision is returned. +// Get all possible collisions along a line. Collisions are not necessarily sorted by +// distance. const vector &CollisionSet::Line(const Point &from, const Point &to, - const Government *pGov, const Body *target, bool all) const + const Government *pGov, const Body *target) const { const int x = from.X(); const int y = from.Y(); @@ -198,7 +169,6 @@ const vector &CollisionSet::Line(const Point &from, const Point &to, const int endGX = endX >> SHIFT; const int endGY = endY >> SHIFT; - Closest closer_result(1.); lineResult.clear(); // Special case, very common: the projectile is contained in one grid cell. @@ -226,13 +196,10 @@ const vector &CollisionSet::Line(const Point &from, const Point &to, Point offset = from - it->body->Position(); const double range = mask.Collide(offset, to - from, it->body->Facing()); - closer_result.TryNearer(range, it->body); - if(range < 1. && all) + if(range < 1.) lineResult.emplace_back(it->body, collisionType, range); } - if(!all && closer_result.GetClosestDistance() < 1.) - lineResult.emplace_back(closer_result.GetClosestBody(), collisionType, closer_result.GetClosestDistance()); return lineResult; } @@ -247,7 +214,7 @@ const vector &CollisionSet::Line(const Point &from, const Point &to, } Point newEnd = from + pVelocity.Unit() * USED_MAX_VELOCITY; - return Line(from, newEnd, pGov, target, all); + return Line(from, newEnd, pGov, target); } // When stepping from one grid cell to the next, we'll go in this direction. @@ -300,13 +267,12 @@ const vector &CollisionSet::Line(const Point &from, const Point &to, Point offset = from - it->body->Position(); const double range = mask.Collide(offset, to - from, it->body->Facing()); - closer_result.TryNearer(range, it->body); - if(range < 1. && all) + if(range < 1.) lineResult.emplace_back(it->body, collisionType, range); } - // Check if we've found a collision or reached the final grid cell. - if((closer_result.GetClosestBody() && !all) || (gx == endGX && gy == endGY)) + // Check if we've reached the final grid cell. + if(gx == endGX && gy == endGY) break; // If not, move to the next one. Check whether rx / mx < ry / my. const int64_t diff = rx * my - ry * mx; @@ -342,8 +308,6 @@ const vector &CollisionSet::Line(const Point &from, const Point &to, } } - if(!all && closer_result.GetClosestBody()) - lineResult.emplace_back(closer_result.GetClosestBody(), collisionType, closer_result.GetClosestDistance()); return lineResult; } diff --git a/source/CollisionSet.h b/source/CollisionSet.h index f9f024c1455b..91feac34d71c 100644 --- a/source/CollisionSet.h +++ b/source/CollisionSet.h @@ -45,15 +45,14 @@ class CollisionSet { // Finish adding objects (and organize them into the final lookup table). void Finish(); - // Get all collisions for the given projectile. Collisions are not necessarily - // sorted by distance. If the projectile is incapable if impacting multiple ships - // in the same frame then only the closest collision is returned. + // Get all possible collisions for the given projectile. Collisions are not necessarily + // sorted by distance. const std::vector &Line(const Projectile &projectile) const; - // Get all collisions along a line. Collisions are not necessarily sorted by - // distance. If the all variable is false then only the closest collision is returned. + // Get all possible collisions along a line. Collisions are not necessarily sorted by + // distance. const std::vector &Line(const Point &from, const Point &to, - const Government *pGov = nullptr, const Body *target = nullptr, bool all = true) const; + const Government *pGov = nullptr, const Body *target = nullptr) const; // Get all objects within the given range of the given point. const std::vector &Circle(const Point ¢er, double radius) const; diff --git a/source/Engine.cpp b/source/Engine.cpp index 7c3e0811ae1c..1515963d6ad7 100644 --- a/source/Engine.cpp +++ b/source/Engine.cpp @@ -2175,6 +2175,11 @@ void Engine::DoCollisions(Projectile &projectile) if(hit && collisionType == CollisionType::SHIP) shipHit = reinterpret_cast(hit)->shared_from_this(); + // Don't collide with carried ships that are disabled and not directly targeted. + if(shipHit && hit != projectile.Target() + && shipHit->CanBeCarried() && shipHit->IsDisabled()) + continue; + // Create the explosion the given distance along the projectile's // motion path for this step. projectile.Explode(visuals, range, hit ? hit->Velocity() : Point()); diff --git a/source/Projectile.cpp b/source/Projectile.cpp index 81f55fcc7e49..0edbfbdd4494 100644 --- a/source/Projectile.cpp +++ b/source/Projectile.cpp @@ -60,7 +60,10 @@ Projectile::Projectile(const Ship &parent, Point position, Angle angle, const We cachedTarget = TargetPtr().get(); if(cachedTarget) + { targetGovernment = cachedTarget->GetGovernment(); + targetDisabled = cachedTarget->IsDisabled(); + } dV = this->angle.Unit() * (weapon->Velocity() + Random::Real() * weapon->RandomVelocity()); velocity += dV; @@ -79,6 +82,7 @@ Projectile::Projectile(const Projectile &parent, const Point &offset, const Angl { government = parent.government; targetGovernment = parent.targetGovernment; + targetDisabled = parent.targetDisabled; hitsRemaining = weapon->PenetrationCount(); cachedTarget = TargetPtr().get(); @@ -137,14 +141,15 @@ void Projectile::Move(vector &visuals, vector &projectiles) // If the target has left the system, stop following it. Also stop if the // target has been captured by a different government. + // Also stop targeting fighters that have become disabled after this projectile was fired. const Ship *target = cachedTarget; if(target) { target = TargetPtr().get(); - if(!target || !target->IsTargetable() || target->GetGovernment() != targetGovernment) + if(!target || !target->IsTargetable() || target->GetGovernment() != targetGovernment || + (!targetDisabled && target->IsDisabled() && target->CanBeCarried())) { - targetShip.reset(); - cachedTarget = nullptr; + BreakTarget(); target = nullptr; } } @@ -380,6 +385,7 @@ void Projectile::BreakTarget() targetShip.reset(); cachedTarget = nullptr; targetGovernment = nullptr; + targetDisabled = false; } diff --git a/source/Projectile.h b/source/Projectile.h index 9d4726f50965..1323c4296989 100644 --- a/source/Projectile.h +++ b/source/Projectile.h @@ -114,6 +114,7 @@ class Projectile : public Body { std::weak_ptr targetShip; const Ship *cachedTarget = nullptr; + bool targetDisabled = false; const Government *targetGovernment = nullptr; // The change in velocity of all stages of this projectile