diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 7da1887481..df247b03e2 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -757,7 +757,7 @@ Trajectory.Speed=100.0 ; floating point value - `Trajectory.Straight.PassDetonate` enables extra detonations when the projectile is traveling. (You can use this when you want the projectile to detonate warheads every other distance/time during the flight.) - `Trajectory.Straight.PassDetonateWarhead` defines the warhead detonated by `Trajectory.Straight.PassDetonate`, and `Trajectory.Straight.PassDetonateDamage` defines the damage caused by `Trajectory.Straight.PassDetonateWarhead`. - `Trajectory.Straight.PassDetonateDelay` controls the delay for detonating the warhead defined by `Trajectory.Straight.Warhead`. - - `Trajectory.Straight.PassDetonateInitialDelay` controls the initial delay for detonating the warhead defined by `Trajectory.Straight.Warhead`. + - `Trajectory.Straight.PassDetonateInitialDelay` controls the initial delay for detonating the warhead defined by `Trajectory.Straight.PassDetonateWarhead`. - `Trajectory.Straight.PassDetonateLocal` controls whether `Trajectory.Straight.PassDetonateWarhead` and weapon's `Warhead` are always detonate at ground level. It will also no longer restrict vertical velocity of the projectile when using `Trajectory.Straight.ConfineAtHeight`. - `Trajectory.Straight.LeadTimeCalculate` controls whether the projectile need to calculate the lead time of the target when firing. Note that this will not affect the facing of the turret. - `Trajectory.Straight.OffsetCoord` controls the offsets of the target. Projectile will aim at this position to attack. It also supports `Inaccurate=yes` and `Trajectory.Straight.LeadTimeCalculate=true` on this basis. @@ -765,7 +765,7 @@ Trajectory.Speed=100.0 ; floating point value - `Trajectory.Straight.MirrorCoord` controls whether `Trajectory.Straight.OffsetCoord` need to mirror the lateral value to adapt to the current burst index. At the same time, the rotation direction calculated by `Trajectory.Straight.RotateCoord` will also be reversed, and the rotation angle between each adjacent projectile on each side will not change as a result. - `Trajectory.Straight.UseDisperseBurst` controls whether the calculation of `Trajectory.Straight.RotateCoord` is based on its superior's `Trajectory.Disperse.WeaponBurst` of the dispersed trajectory, rather than `Burst` of the weapon. If this value is not appropriate, it will result in unsatisfactory visual displays. - `Trajectory.Straight.AxisOfRotation` controls the rotation axis when calculating `Trajectory.Straight.RotateCoord`. The axis will rotates with the unit orientation or the vector that from target position to the source position. - - `Trajectory.Straight.ProximityImpact` controls the initial proximity fuse times. When there are enough remaining times and the projectile approaches another valid target, it will detonate a warhead defined by `Trajectory.Straight.Warhead` on it. If the times is about to run out, it will also detonate itself at its location. This function can be cancelled by setting to 0. A negative integer means unlimited times. By the way, you can use the weapon's `Warhead` with low versus only to aim at the target, and use the `Trajectory.Straight.ProximityWarhead` to causing actual harm. (You can use this to cause non repeated damage to all units encountered during the flight of the projectile.) + - `Trajectory.Straight.ProximityImpact` controls the initial proximity fuse times. When there are enough remaining times and the projectile approaches another valid target, it will detonate a warhead defined by `Trajectory.Straight.ProximityWarhead` on it. If the times is about to run out, it will also detonate itself at its location. This function can be cancelled by setting to 0. A negative integer means unlimited times. By the way, you can use the weapon's `Warhead` with low versus only to aim at the target, and use the `Trajectory.Straight.ProximityWarhead` to causing actual harm. (You can use this to cause non repeated damage to all units encountered during the flight of the projectile.) - `Trajectory.Straight.ProximityWarhead` defines the warhead detonated by `Trajectory.Straight.ProximityImpact`, and `Trajectory.Straight.ProximityDamage` defines the damage caused by `Trajectory.Straight.ProximityWarhead`. - `Trajectory.Straight.ProximityRadius` controls the range of proximity fuse. It can NOT be set as a negative integer. - `Trajectory.Straight.ProximityDirect` controls whether let the target receive damage instead of detonating the warhead. diff --git a/src/Ext/Bullet/Trajectories/StraightTrajectory.cpp b/src/Ext/Bullet/Trajectories/StraightTrajectory.cpp index 8b80c5964a..effa714cd3 100644 --- a/src/Ext/Bullet/Trajectories/StraightTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/StraightTrajectory.cpp @@ -787,7 +787,8 @@ void StraightTrajectory::PrepareForDetonateAt(BulletClass* pBullet, HouseClass* return; // Step 1: Find valid targets on the ground within range. - std::vector recCellClass = PhobosTrajectoryType::GetCellsInProximityRadius(pBullet, pType->ProximityRadius.Get()); + const auto radius = pType->ProximityRadius.Get(); + std::vector recCellClass = PhobosTrajectoryType::GetCellsInProximityRadius(pBullet, radius); const size_t cellSize = recCellClass.size() * 2; size_t vectSize = cellSize; size_t thisSize = 0; @@ -798,10 +799,11 @@ void StraightTrajectory::PrepareForDetonateAt(BulletClass* pBullet, HouseClass* static_cast(pBullet->Velocity.Y), static_cast(pBullet->Velocity.Z) }; + const auto velocitySq = velocityCrd.MagnitudeSquared(); + const auto pTarget = pBullet->Target; std::vector validTechnos; validTechnos.reserve(vectSize); - const auto pTarget = pBullet->Target; for (const auto& pRecCell : recCellClass) { @@ -824,19 +826,23 @@ void StraightTrajectory::PrepareForDetonateAt(BulletClass* pBullet, HouseClass* if (!pType->ProximityAllies && pOwner && pOwner->IsAlliedWith(pTechno->Owner) && pTechno != pTarget) continue; - const auto distanceCrd = pTechno->GetCoords() - pBullet->SourceCoords; - const auto locationCrd = (velocityCrd + (pBullet->Location - pBullet->SourceCoords)); - const auto terminalCrd = distanceCrd - locationCrd; - auto distance = locationCrd.MagnitudeSquared(); // Not true distance yet. + // Check distance + const auto targetCrd = pTechno->GetCoords(); + const auto pathCrd = targetCrd - pBullet->SourceCoords; + + if (pathCrd * velocityCrd < 0) // In front of the techno + continue; + + const auto distanceCrd = targetCrd - pBullet->Location; + const auto nextDistanceCrd = distanceCrd - velocityCrd; - // Between front and back - if (distanceCrd * velocityCrd < 0 || terminalCrd * velocityCrd > 0) + if (nextDistanceCrd * velocityCrd > 0) // Behind the bullet continue; - distance = (distance > 1e-10) ? sqrt(distanceCrd.CrossProduct(terminalCrd).MagnitudeSquared() / distance) : distanceCrd.Magnitude(); + const auto cross = distanceCrd.CrossProduct(nextDistanceCrd).MagnitudeSquared(); + const auto distance = (velocitySq > 1e-10) ? sqrt(cross / velocitySq) : distanceCrd.Magnitude(); - // Between left and right (cylindrical) - if (technoType != AbstractType::Building && distance > pType->ProximityRadius.Get()) + if (technoType != AbstractType::Building && distance > radius) // In the cylinder continue; if (thisSize >= vectSize) @@ -854,26 +860,35 @@ void StraightTrajectory::PrepareForDetonateAt(BulletClass* pBullet, HouseClass* if (pType->ProximityFlight) { const auto airTracker = &AircraftTrackerClass::Instance; - airTracker->FillCurrentVector(MapClass::Instance->GetCellAt(pBullet->Location + velocityCrd * 0.5), static_cast((pType->ProximityRadius.Get() + pType->Trajectory_Speed / 2) / Unsorted::LeptonsPerCell)); + airTracker->FillCurrentVector(MapClass::Instance->GetCellAt(pBullet->Location + velocityCrd * 0.5), + Game::F2I(sqrt(radius * radius + (velocitySq / 4)) / Unsorted::LeptonsPerCell)); for (auto pTechno = airTracker->Get(); pTechno; pTechno = airTracker->Get()) { if (!pTechno->IsAlive || !pTechno->IsOnMap || pTechno->Health <= 0 || pTechno->InLimbo || pTechno->IsSinking) continue; + // Not directly harming friendly forces if (!pType->ProximityAllies && pOwner && pOwner->IsAlliedWith(pTechno->Owner) && pTechno != pTarget) continue; - const auto distanceCrd = pTechno->GetCoords() - pBullet->Location; - const auto terminalCrd = distanceCrd - velocityCrd; - auto distance = velocityCrd.MagnitudeSquared(); // Not true distance yet. + // Check distance + const auto targetCrd = pTechno->GetCoords(); + const auto pathCrd = targetCrd - pBullet->SourceCoords; + + if (pathCrd * velocityCrd < 0) // In front of the techno + continue; + + const auto distanceCrd = targetCrd - pBullet->Location; + const auto nextDistanceCrd = distanceCrd - velocityCrd; - if (distanceCrd * velocityCrd < 0 || terminalCrd * velocityCrd > 0) + if (nextDistanceCrd * velocityCrd > 0) // Behind the bullet continue; - distance = (distance > 1e-10) ? sqrt(distanceCrd.CrossProduct(terminalCrd).MagnitudeSquared() / distance) : distanceCrd.Magnitude(); + const auto cross = distanceCrd.CrossProduct(nextDistanceCrd).MagnitudeSquared(); + const auto distance = (velocitySq > 1e-10) ? sqrt(cross / velocitySq) : distanceCrd.Magnitude(); - if (distance > pType->ProximityRadius.Get()) + if (distance > radius) // In the cylinder continue; if (thisSize >= vectSize)