From 613ccb2fb2c5f5f3338c5bbdd22018e8b6b5fc82 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Wed, 4 Sep 2024 14:53:17 +0800 Subject: [PATCH 01/32] Core --- .../Trajectories/ParabolaTrajectory.cpp | 1003 +++++++++++++++++ .../Bullet/Trajectories/ParabolaTrajectory.h | 171 +++ 2 files changed, 1174 insertions(+) create mode 100644 src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp create mode 100644 src/Ext/Bullet/Trajectories/ParabolaTrajectory.h diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp new file mode 100644 index 0000000000..b1bd2a0603 --- /dev/null +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp @@ -0,0 +1,1003 @@ +#include "ParabolaTrajectory.h" +#include +#include +#include +#include +#include + +bool ParabolaTrajectoryType::Load(PhobosStreamReader& Stm, bool RegisterForChange) +{ + this->PhobosTrajectoryType::Load(Stm, false); + + Stm + .Process(this->DetonationDistance, false) + .Process(this->TargetSnapDistance, false) + .Process(this->OpenFireMode, false) + .Process(this->ThrowHeight, false) + .Process(this->LaunchAngle, false) + .Process(this->LeadTimeCalculate, false) + .Process(this->LeadTimeSimplify, false) + .Process(this->LeadTimeMultiplier, false) + .Process(this->BounceTimes, false) + .Process(this->BounceOnWater, false) + .Process(this->BounceDetonate, false) + .Process(this->BounceAttenuation, false) + .Process(this->BounceCoefficient, false) + .Process(this->OffsetCoord, false) + .Process(this->RotateCoord, false) + .Process(this->MirrorCoord, false) + .Process(this->UseDisperseBurst, false) + .Process(this->AxisOfRotation, false) + ; + + return true; +} + +bool ParabolaTrajectoryType::Save(PhobosStreamWriter& Stm) const +{ + this->PhobosTrajectoryType::Save(Stm); + + Stm + .Process(this->DetonationDistance) + .Process(this->TargetSnapDistance) + .Process(this->OpenFireMode) + .Process(this->ThrowHeight) + .Process(this->LaunchAngle) + .Process(this->LeadTimeCalculate) + .Process(this->LeadTimeSimplify) + .Process(this->LeadTimeMultiplier) + .Process(this->BounceTimes) + .Process(this->BounceOnWater) + .Process(this->BounceDetonate) + .Process(this->BounceAttenuation) + .Process(this->BounceCoefficient) + .Process(this->OffsetCoord) + .Process(this->RotateCoord) + .Process(this->MirrorCoord) + .Process(this->UseDisperseBurst) + .Process(this->AxisOfRotation) + ; + + return true; +} + +PhobosTrajectory* ParabolaTrajectoryType::CreateInstance() const +{ + return new ParabolaTrajectory(this); +} + +void ParabolaTrajectoryType::Read(CCINIClass* const pINI, const char* pSection) +{ + INI_EX exINI(pINI); + this->DetonationDistance.Read(exINI, pSection, "Trajectory.Parabola.DetonationDistance"); + this->TargetSnapDistance.Read(exINI, pSection, "Trajectory.Parabola.TargetSnapDistance"); + this->OpenFireMode.Read(exINI, pSection, "Trajectory.Parabola.OpenFireMode"); + this->ThrowHeight.Read(exINI, pSection, "Trajectory.Parabola.ThrowHeight"); + this->LaunchAngle.Read(exINI, pSection, "Trajectory.Parabola.LaunchAngle"); + this->LeadTimeCalculate.Read(exINI, pSection, "Trajectory.Parabola.LeadTimeCalculate"); + this->LeadTimeSimplify.Read(exINI, pSection, "Trajectory.Parabola.LeadTimeSimplify"); + this->LeadTimeMultiplier.Read(exINI, pSection, "Trajectory.Parabola.LeadTimeMultiplier"); + this->BounceTimes.Read(exINI, pSection, "Trajectory.Parabola.BounceTimes"); + this->BounceOnWater.Read(exINI, pSection, "Trajectory.Parabola.BounceOnWater"); + this->BounceDetonate.Read(exINI, pSection, "Trajectory.Parabola.BounceDetonate"); + this->BounceAttenuation.Read(exINI, pSection, "Trajectory.Parabola.BounceAttenuation"); + this->BounceCoefficient.Read(exINI, pSection, "Trajectory.Parabola.BounceCoefficient"); + this->OffsetCoord.Read(exINI, pSection, "Trajectory.Parabola.OffsetCoord"); + this->RotateCoord.Read(exINI, pSection, "Trajectory.Parabola.RotateCoord"); + this->MirrorCoord.Read(exINI, pSection, "Trajectory.Parabola.MirrorCoord"); + this->UseDisperseBurst.Read(exINI, pSection, "Trajectory.Parabola.UseDisperseBurst"); + this->AxisOfRotation.Read(exINI, pSection, "Trajectory.Parabola.AxisOfRotation"); +} + +bool ParabolaTrajectory::Load(PhobosStreamReader& Stm, bool RegisterForChange) +{ + this->PhobosTrajectory::Load(Stm, false); + + Stm + .Process(this->DetonationDistance) + .Process(this->TargetSnapDistance) + .Process(this->OpenFireMode) + .Process(this->ThrowHeight) + .Process(this->LaunchAngle) + .Process(this->LeadTimeCalculate) + .Process(this->LeadTimeSimplify) + .Process(this->LeadTimeMultiplier) + .Process(this->BounceTimes) + .Process(this->BounceOnWater) + .Process(this->BounceDetonate) + .Process(this->BounceAttenuation) + .Process(this->BounceCoefficient) + .Process(this->OffsetCoord) + .Process(this->RotateCoord) + .Process(this->MirrorCoord) + .Process(this->UseDisperseBurst) + .Process(this->AxisOfRotation) + .Process(this->ShouldDetonate) + .Process(this->ShouldBounce) + .Process(this->NeedExtraCheck) + .Process(this->LastTargetCoord) + .Process(this->CurrentBurst) + .Process(this->CountOfBurst) + .Process(this->WaitOneFrame) + .Process(this->LastVelocity) + ; + + return true; +} + +bool ParabolaTrajectory::Save(PhobosStreamWriter& Stm) const +{ + this->PhobosTrajectory::Save(Stm); + + Stm + .Process(this->DetonationDistance) + .Process(this->TargetSnapDistance) + .Process(this->OpenFireMode) + .Process(this->ThrowHeight) + .Process(this->LaunchAngle) + .Process(this->LeadTimeCalculate) + .Process(this->LeadTimeSimplify) + .Process(this->LeadTimeMultiplier) + .Process(this->BounceTimes) + .Process(this->BounceOnWater) + .Process(this->BounceDetonate) + .Process(this->BounceAttenuation) + .Process(this->BounceCoefficient) + .Process(this->OffsetCoord) + .Process(this->RotateCoord) + .Process(this->MirrorCoord) + .Process(this->UseDisperseBurst) + .Process(this->AxisOfRotation) + .Process(this->ShouldDetonate) + .Process(this->ShouldBounce) + .Process(this->NeedExtraCheck) + .Process(this->LastTargetCoord) + .Process(this->CurrentBurst) + .Process(this->CountOfBurst) + .Process(this->WaitOneFrame) + .Process(this->LastVelocity) + ; + + return true; +} + +void ParabolaTrajectory::OnUnlimbo(BulletClass* pBullet, CoordStruct* pCoord, BulletVelocity* pVelocity) +{ + auto const pType = this->GetTrajectoryType(pBullet); + + this->DetonationDistance = pType->DetonationDistance; + this->TargetSnapDistance = pType->TargetSnapDistance; + this->OpenFireMode = pType->OpenFireMode; + this->ThrowHeight = pType->ThrowHeight > 0 ? pType->ThrowHeight : 600; + this->LaunchAngle = pType->LaunchAngle; + this->LeadTimeCalculate = pType->LeadTimeCalculate; + this->LeadTimeSimplify = pType->LeadTimeSimplify; + this->LeadTimeMultiplier = pType->LeadTimeMultiplier; + this->BounceTimes = pType->BounceTimes; + this->BounceOnWater = pType->BounceOnWater; + this->BounceDetonate = pType->BounceDetonate; + this->BounceAttenuation = pType->BounceAttenuation; + this->BounceCoefficient = pType->BounceCoefficient; + this->OffsetCoord = pType->OffsetCoord; + this->RotateCoord = pType->RotateCoord; + this->MirrorCoord = pType->MirrorCoord; + this->UseDisperseBurst = pType->UseDisperseBurst; + this->AxisOfRotation = pType->AxisOfRotation; + this->ShouldDetonate = false; + this->ShouldBounce = false; + this->NeedExtraCheck = false; + this->LastTargetCoord = pBullet->TargetCoords; + this->CurrentBurst = 0; + this->CountOfBurst = 0; + this->LastVelocity = BulletVelocity::Empty; + pBullet->Velocity = BulletVelocity::Empty; + + if (WeaponTypeClass* const pWeapon = pBullet->WeaponType) + this->CountOfBurst = pWeapon->Burst; + + if (TechnoClass* const pOwner = pBullet->Owner) + { + this->CurrentBurst = pOwner->CurrentBurstIndex; + + if (this->MirrorCoord && pOwner->CurrentBurstIndex % 2 == 1) + this->OffsetCoord.Y = -(this->OffsetCoord.Y); + } + + if (!this->LeadTimeCalculate || !abstract_cast(pBullet->Target)) + this->PrepareForOpenFire(pBullet); + else + this->WaitOneFrame.Start(1); +} + +bool ParabolaTrajectory::OnAI(BulletClass* pBullet) +{ + if (this->WaitOneFrame.IsTicking() && this->BulletPrepareCheck(pBullet)) + return false; + + if (this->ShouldDetonate || (this->DetonationDistance > 0 && pBullet->TargetCoords.DistanceFrom(pBullet->Location) < this->DetonationDistance)) + return true; + + CellClass* const pCell = MapClass::Instance->TryGetCellAt(pBullet->Location); + + if (!pCell) + return true; + + const double gravity = BulletTypeExt::GetAdjustedGravity(pBullet->Type); + + if (this->ShouldBounce && this->BounceTimes > 0) + return (pCell->LandType == LandType::Water && !this->BounceOnWater) || this->CalculateBulletVelocityAfterBounce(pBullet, pCell, gravity); + + return this->BulletDetonateLastCheck(pBullet, gravity); +} + +void ParabolaTrajectory::OnAIPreDetonate(BulletClass* pBullet) +{ + if (this->TargetSnapDistance <= 0) + return; + + const ObjectClass* const pTarget = abstract_cast(pBullet->Target); + const CoordStruct coords = pTarget ? pTarget->GetCoords() : pBullet->Data.Location; + + if (coords.DistanceFrom(pBullet->Location) <= this->TargetSnapDistance) + { + auto const pExt = BulletExt::ExtMap.Find(pBullet); + pExt->SnappedToTarget = true; + pBullet->SetLocation(coords); + } + else if (const int cellHeight = MapClass::Instance->GetCellFloorHeight(pBullet->Location)) + { + if (pBullet->Location.Z < cellHeight) + pBullet->SetLocation(CoordStruct{ pBullet->Location.X, pBullet->Location.Y, cellHeight }); + } +} + +void ParabolaTrajectory::OnAIVelocity(BulletClass* pBullet, BulletVelocity* pSpeed, BulletVelocity* pPosition) +{ + pSpeed->Z += BulletTypeExt::GetAdjustedGravity(pBullet->Type); // Seems like this is useless +} + +TrajectoryCheckReturnType ParabolaTrajectory::OnAITargetCoordCheck(BulletClass* pBullet) +{ + return TrajectoryCheckReturnType::SkipGameCheck; +} + +TrajectoryCheckReturnType ParabolaTrajectory::OnAITechnoCheck(BulletClass* pBullet, TechnoClass* pTechno) +{ + return TrajectoryCheckReturnType::SkipGameCheck; +} + +void ParabolaTrajectory::PrepareForOpenFire(BulletClass* pBullet) +{ + const AbstractClass* const pTarget = pBullet->Target; + bool leadTimeCalculate = this->LeadTimeCalculate && pTarget; + CoordStruct theTargetCoords = leadTimeCalculate ? pTarget->GetCoords() : pBullet->TargetCoords; + CoordStruct theSourceCoords = leadTimeCalculate ? pBullet->Location : pBullet->SourceCoords; + leadTimeCalculate &= theTargetCoords != this->LastTargetCoord; + double rotateAngle = 0.0; + + if (!this->LeadTimeCalculate && theTargetCoords == theSourceCoords && pBullet->Owner) //For disperse. + { + const CoordStruct theOwnerCoords = pBullet->Owner->GetCoords(); + rotateAngle = Math::atan2(theTargetCoords.Y - theOwnerCoords.Y , theTargetCoords.X - theOwnerCoords.X); + } + else + { + rotateAngle = Math::atan2(theTargetCoords.Y - theSourceCoords.Y , theTargetCoords.X - theSourceCoords.X); + } + + if (this->OffsetCoord != CoordStruct::Empty) + { + theTargetCoords.X += static_cast(this->OffsetCoord.X * Math::cos(rotateAngle) + this->OffsetCoord.Y * Math::sin(rotateAngle)); + theTargetCoords.Y += static_cast(this->OffsetCoord.X * Math::sin(rotateAngle) - this->OffsetCoord.Y * Math::cos(rotateAngle)); + theTargetCoords.Z += this->OffsetCoord.Z; + } + + if (pBullet->Type->Inaccurate) + { + auto const pTypeExt = BulletTypeExt::ExtMap.Find(pBullet->Type); + const double offsetMult = 0.0004 * theSourceCoords.DistanceFrom(theTargetCoords); + const int offsetMin = static_cast(offsetMult * pTypeExt->BallisticScatter_Min.Get(Leptons(0))); + const int offsetMax = static_cast(offsetMult * pTypeExt->BallisticScatter_Max.Get(Leptons(RulesClass::Instance->BallisticScatter))); + const int offsetDistance = ScenarioClass::Instance->Random.RandomRanged(offsetMin, offsetMax); + theTargetCoords = MapClass::GetRandomCoordsNear(theTargetCoords, offsetDistance, false); + } + + pBullet->TargetCoords = theTargetCoords; + const double gravity = BulletTypeExt::GetAdjustedGravity(pBullet->Type); + + if (gravity <= 0.0) + { + pBullet->Velocity = BulletVelocity::Empty; + this->ShouldDetonate = true; + return; + } + + if (leadTimeCalculate) + this->CalculateBulletVelocityLeadTime(pBullet, &theSourceCoords, gravity); + else + this->CalculateBulletVelocityRightNow(pBullet, &theSourceCoords, gravity); + + if (!this->UseDisperseBurst && this->RotateCoord != 0 && this->CountOfBurst > 1) + { + BulletVelocity rotationAxis + { + this->AxisOfRotation.X * Math::cos(rotateAngle) + this->AxisOfRotation.Y * Math::sin(rotateAngle), + this->AxisOfRotation.X * Math::sin(rotateAngle) - this->AxisOfRotation.Y * Math::cos(rotateAngle), + static_cast(this->AxisOfRotation.Z) + }; + + const double rotationAxisLengthSquared = rotationAxis.MagnitudeSquared(); + + if (rotationAxisLengthSquared != 0) + { + double extraRotate = 0; + rotationAxis *= 1 / sqrt(rotationAxisLengthSquared); + + if (this->MirrorCoord) + { + if (pBullet->Owner && pBullet->Owner->CurrentBurstIndex % 2 == 1) + rotationAxis *= -1; + + extraRotate = Math::Pi * (this->RotateCoord * ((this->CurrentBurst / 2) / (this->CountOfBurst - 1.0) - 0.5)) / 180; + } + else + { + extraRotate = Math::Pi * (this->RotateCoord * (this->CurrentBurst / (this->CountOfBurst - 1.0) - 0.5)) / 180; + } + + const double cosRotate = Math::cos(extraRotate); + pBullet->Velocity = (pBullet->Velocity * cosRotate) + (rotationAxis * ((1 - cosRotate) * (pBullet->Velocity * rotationAxis))) + (rotationAxis.CrossProduct(pBullet->Velocity) * Math::sin(extraRotate)); + } + } +} + +bool ParabolaTrajectory::BulletPrepareCheck(BulletClass* pBullet) +{ + if (this->WaitOneFrame.HasTimeLeft()) + return true; + + this->PrepareForOpenFire(pBullet); + this->WaitOneFrame.Stop(); + + return false; +} + +void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, CoordStruct* pSourceCoords, double gravity) +{ + if (this->LeadTimeSimplify) + { + int leadTime = 0; + + // Only simple guess, not exact solution + switch (this->OpenFireMode) + { + case 1: + case 4: + { + leadTime = static_cast(sqrt((this->ThrowHeight << 1) / gravity) * 1.25); + break; + } + case 2: + { + double radian = this->LaunchAngle * Math::Pi / 180.0; + radian = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? (Math::HalfPi / 3) : radian; + const double factor = cos(radian); + + if (abs(factor) < 1e-10) + break; + + const double mult = sin(2 * radian); + const double velocity = abs(mult) > 1e-10 ? sqrt((Unsorted::LeptonsPerCell << 2) * gravity / mult) : 0.0; + + if (velocity < 1e-10) + break; + + leadTime = static_cast((Unsorted::LeptonsPerCell << 2) / (velocity * factor)); + break; + } + default: + { + leadTime = static_cast((Unsorted::LeptonsPerCell << 2) / this->GetTrajectorySpeed(pBullet)); + break; + } + } + + pBullet->TargetCoords += (pBullet->Target->GetCoords() - this->LastTargetCoord) * (this->LeadTimeMultiplier * leadTime); + this->CalculateBulletVelocityRightNow(pBullet, pSourceCoords, gravity); + return; + } + + CoordStruct targetCoords = pBullet->Target->GetCoords(); + CoordStruct offsetCoords = pBullet->TargetCoords - targetCoords; + const double speedFixMult = this->LeadTimeMultiplier * 0.75; // A coefficient that should not exist here normally, but even so, there are still errors + + switch (this->OpenFireMode) + { + case 1: // Fixed max height and aim at the target + { + const double meetTime = this->SearchFixedHeightMeetTime(pSourceCoords, &targetCoords, &offsetCoords, gravity); + pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); + const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; + + if (meetTime <= 0.0 || destinationCoords.Magnitude() <= 0.0) + break; + + pBullet->Velocity.X = destinationCoords.X / meetTime; + pBullet->Velocity.Y = destinationCoords.Y / meetTime; + + const int sourceHeight = pSourceCoords->Z, targetHeight = sourceHeight + destinationCoords.Z; + const int maxHeight = destinationCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; + pBullet->Velocity.Z = sqrt(2 * gravity * (maxHeight - sourceHeight)) + gravity / 2; + this->CheckIfNeedExtraCheck(pBullet); + return; + } + case 2: // Fixed fire angle and aim at the target + { + double radian = this->LaunchAngle * Math::Pi / 180.0; + radian = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? (Math::HalfPi / 3) : radian; + + const double meetTime = this->SearchFixedAngleMeetTime(pSourceCoords, &targetCoords, &offsetCoords, radian, gravity); + pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); + const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; + + if (meetTime <= 0.0 || destinationCoords.Magnitude() <= 0.0) + break; + + pBullet->Velocity.X = destinationCoords.X / meetTime; + pBullet->Velocity.Y = destinationCoords.Y / meetTime; + + const double horizontalDistance = Point2D{ destinationCoords.X, destinationCoords.Y }.Magnitude(); + const double horizontalVelocity = horizontalDistance / meetTime; + pBullet->Velocity.Z = horizontalVelocity * tan(radian) + gravity / 2; + this->CheckIfNeedExtraCheck(pBullet); + return; + } + case 3: // Fixed horizontal speed and fixed max height + { + const double horizontalSpeed = this->GetTrajectorySpeed(pBullet); + + const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, horizontalSpeed); + pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); + const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; + + if (meetTime <= 0.0 || destinationCoords.Magnitude() <= 0.0) + break; + + const double horizontalDistance = Point2D{ destinationCoords.X, destinationCoords.Y }.Magnitude(); + const double mult = horizontalDistance > 0.0 ? horizontalSpeed / horizontalDistance : 1.0; + + pBullet->Velocity.X = destinationCoords.X * mult; + pBullet->Velocity.Y = destinationCoords.Y * mult; + + const int sourceHeight = pSourceCoords->Z, targetHeight = sourceHeight + destinationCoords.Z; + const int maxHeight = destinationCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; + pBullet->Velocity.Z = sqrt(2 * gravity * (maxHeight - sourceHeight)) + gravity / 2; + this->CheckIfNeedExtraCheck(pBullet); + return; + } + case 4: // Fixed max height and fixed fire angle + { + const double meetTime = this->SearchFixedHeightMeetTime(pSourceCoords, &targetCoords, &offsetCoords, gravity); + pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); + const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; + + if (meetTime <= 0.0 || destinationCoords.Magnitude() <= 0.0) + break; + + const int sourceHeight = pSourceCoords->Z, targetHeight = sourceHeight + destinationCoords.Z; + const int maxHeight = destinationCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; + pBullet->Velocity.Z = sqrt(2 * gravity * (maxHeight - sourceHeight)) + gravity / 2; + + double radian = this->LaunchAngle * Math::Pi / 180.0; + radian = (radian >= Math::HalfPi || radian <= 0.0) ? (Math::HalfPi / 3) : radian; + + const double horizontalDistance = Point2D{ destinationCoords.X, destinationCoords.Y }.Magnitude(); + const double mult = (pBullet->Velocity.Z / tan(radian)) / horizontalDistance; + + pBullet->Velocity.X = destinationCoords.X * mult; + pBullet->Velocity.Y = destinationCoords.Y * mult; + this->CheckIfNeedExtraCheck(pBullet); + return; + } + case 5: // Fixed horizontal speed and fixed fire angle + { + const double horizontalSpeed = this->GetTrajectorySpeed(pBullet); + + const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, horizontalSpeed); + pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); + const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; + + if (meetTime <= 0.0 || destinationCoords.Magnitude() <= 0.0) + break; + + const double horizontalDistance = Point2D{ destinationCoords.X, destinationCoords.Y }.Magnitude(); + const double mult = horizontalDistance > 0.0 ? horizontalSpeed / horizontalDistance : 1.0; + + pBullet->Velocity.X = destinationCoords.X * mult; + pBullet->Velocity.Y = destinationCoords.Y * mult; + const double horizontalVelocity = horizontalDistance * mult; + + double radian = this->LaunchAngle * Math::Pi / 180.0; + radian = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? (Math::HalfPi / 3) : radian; + pBullet->Velocity.Z = horizontalVelocity * tan(radian) + gravity / 2; + this->CheckIfNeedExtraCheck(pBullet); + return; + } + default: // Fixed horizontal speed and aim at the target + { + const double horizontalSpeed = this->GetTrajectorySpeed(pBullet); + + const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, horizontalSpeed); + pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); + const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; + + if (meetTime <= 0.0 || destinationCoords.Magnitude() <= 0.0) + break; + + const double horizontalDistance = Point2D{ destinationCoords.X, destinationCoords.Y }.Magnitude(); + const double mult = horizontalDistance > 0.0 ? horizontalSpeed / horizontalDistance : 1.0; + + pBullet->Velocity.X = destinationCoords.X * mult; + pBullet->Velocity.Y = destinationCoords.Y * mult; + pBullet->Velocity.Z = destinationCoords.Z * mult + (gravity * horizontalDistance) / (2 * horizontalSpeed) + gravity / 2; + this->CheckIfNeedExtraCheck(pBullet); + return; + } + } + + pBullet->TargetCoords = targetCoords + offsetCoords; + this->CalculateBulletVelocityRightNow(pBullet, pSourceCoords, gravity); +} + +void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, CoordStruct* pSourceCoords, double gravity) +{ + const CoordStruct distanceCoords = pBullet->TargetCoords - *pSourceCoords; + const double distance = distanceCoords.Magnitude(); + const double horizontalDistance = Point2D{ distanceCoords.X, distanceCoords.Y }.Magnitude(); + + if (distance <= 0.0) + { + pBullet->Velocity = BulletVelocity::Empty; + this->ShouldDetonate = true; + return; + } + + switch (this->OpenFireMode) + { + case 1: // Fixed max height and aim at the target + { + const int sourceHeight = pSourceCoords->Z, targetHeight = pBullet->TargetCoords.Z; + const int maxHeight = distanceCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; + pBullet->Velocity.Z = sqrt(2 * gravity * (maxHeight - sourceHeight)); + + const double meetTime = sqrt(2 * (maxHeight - sourceHeight) / gravity) + sqrt(2 * (maxHeight - targetHeight) / gravity); + pBullet->Velocity.X = distanceCoords.X / meetTime; + pBullet->Velocity.Y = distanceCoords.Y / meetTime; + break; + } + case 2: // Fixed fire angle and aim at the target + { + double radian = this->LaunchAngle * Math::Pi / 180.0; + double velocity = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? 100.0 : this->SearchVelocity(horizontalDistance, distanceCoords.Z, radian, gravity); + pBullet->Velocity.Z = velocity * sin(radian); + + const double mult = velocity * cos(radian) / horizontalDistance; + pBullet->Velocity.X = distanceCoords.X * mult; + pBullet->Velocity.Y = distanceCoords.Y * mult; + break; + } + case 3: // Fixed horizontal speed and fixed max height + { + const int sourceHeight = pSourceCoords->Z, targetHeight = pBullet->TargetCoords.Z; + const int maxHeight = distanceCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; + pBullet->Velocity.Z = sqrt(2 * gravity * (maxHeight - sourceHeight)); + + const double horizontalSpeed = this->GetTrajectorySpeed(pBullet); + const double mult = horizontalDistance > 0.0 ? horizontalSpeed / horizontalDistance : 1.0; + + pBullet->Velocity.X = distanceCoords.X * mult; + pBullet->Velocity.Y = distanceCoords.Y * mult; + break; + } + case 4: // Fixed max height and fixed fire angle + { + const int sourceHeight = pSourceCoords->Z, targetHeight = pBullet->TargetCoords.Z; + const int maxHeight = distanceCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; + pBullet->Velocity.Z = sqrt(2 * gravity * (maxHeight - sourceHeight)); + + double radian = this->LaunchAngle * Math::Pi / 180.0; + radian = (radian >= Math::HalfPi || radian <= 0.0) ? (Math::HalfPi / 3) : radian; + const double mult = (pBullet->Velocity.Z / tan(radian)) / horizontalDistance; + + pBullet->Velocity.X = distanceCoords.X * mult; + pBullet->Velocity.Y = distanceCoords.Y * mult; + break; + } + case 5: // Fixed horizontal speed and fixed fire angle + { + const double horizontalSpeed = this->GetTrajectorySpeed(pBullet); + const double mult = horizontalDistance > 0.0 ? horizontalSpeed / horizontalDistance : 1.0; + + pBullet->Velocity.X = distanceCoords.X * mult; + pBullet->Velocity.Y = distanceCoords.Y * mult; + + double radian = this->LaunchAngle * Math::Pi / 180.0; + radian = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? (Math::HalfPi / 3) : radian; + pBullet->Velocity.Z = horizontalSpeed * tan(radian); + break; + } + default: // Fixed horizontal speed and aim at the target + { + const double horizontalSpeed = this->GetTrajectorySpeed(pBullet); + const double mult = horizontalDistance > 0.0 ? horizontalSpeed / horizontalDistance : 1.0; + + pBullet->Velocity.X = distanceCoords.X * mult; + pBullet->Velocity.Y = distanceCoords.Y * mult; + pBullet->Velocity.Z = distanceCoords.Z * mult + (gravity * horizontalDistance) / (2 * horizontalSpeed); + break; + } + } + + this->CheckIfNeedExtraCheck(pBullet); + pBullet->Velocity.Z += gravity / 2; // Offset the gravity effect of the first time update +} + +void ParabolaTrajectory::CheckIfNeedExtraCheck(BulletClass* pBullet) +{ + switch (this->OpenFireMode) + { + case 1: // Fixed max height and aim at the target + case 2: // Fixed fire angle and aim at the target + case 4: // Fixed max height and fixed fire angle + { + this->NeedExtraCheck = Vector2D{ pBullet->Velocity.X, pBullet->Velocity.Y }.MagnitudeSquared() > 65536.0; + break; + } + default: // Fixed horizontal speed and blabla + { + this->NeedExtraCheck = this->GetTrajectorySpeed(pBullet) > 256.0; + break; + } + } +} + +double ParabolaTrajectory::SearchVelocity(double horizontalDistance, int distanceCoordsZ, double radian, double gravity) +{ + const double mult = sin(2 * radian); + double velocity = abs(mult) > 1e-10 ? sqrt(horizontalDistance * gravity / mult) : 0.0; + velocity += distanceCoordsZ / gravity; + velocity = velocity > 10.0 ? velocity : 10.0; + const double delta = 1e-6; + + for (int i = 0; i < 10; ++i) // Newton iteration method + { + const double differential = this->CheckVelocityEquation(horizontalDistance, distanceCoordsZ, velocity, radian, gravity); + const double dDifferential = (this->CheckVelocityEquation(horizontalDistance, distanceCoordsZ, (velocity + delta), radian, gravity) - differential) / delta; + + if (abs(dDifferential) < 1e-10) // Unacceptable divisor + return velocity; + + const double difference = differential / dDifferential; + const double velocityNew = velocity - difference; + + if (abs(difference) < 8.0) // Tolerable error + return velocityNew; + + velocity = velocityNew; + } + + return 10.0; // Unsolvable +} + +double ParabolaTrajectory::CheckVelocityEquation(double horizontalDistance, int distanceCoordsZ, double velocity, double radian, double gravity) +{ + const double horizontalVelocity = velocity * cos(radian); + const double verticalVelocity = velocity * sin(radian); + + const double upTime = verticalVelocity / gravity; + const double maxHeight = 0.5 * verticalVelocity * upTime; + const double downTime = sqrt(2 * (maxHeight - distanceCoordsZ) / gravity); + const double wholeTime = horizontalDistance / horizontalVelocity; + + return wholeTime - (upTime + downTime); +} + +double ParabolaTrajectory::SolveFixedSpeedMeetTime(CoordStruct* pSourceCrd, CoordStruct* pTargetCrd, CoordStruct* pOffsetCrd, double horizontalSpeed) +{ + const Point2D targetSpeedCrd { pTargetCrd->X - this->LastTargetCoord.X, pTargetCrd->Y - this->LastTargetCoord.Y }; + const Point2D destinationCrd { pTargetCrd->X + pOffsetCrd->X - pSourceCrd->X, pTargetCrd->Y + pOffsetCrd->Y - pSourceCrd->Y }; + const double divisor = (targetSpeedCrd.MagnitudeSquared() - horizontalSpeed * horizontalSpeed) * 2; + const double factor = 2 * (targetSpeedCrd * destinationCrd); + const double delta = factor * factor - 2 * divisor * destinationCrd.MagnitudeSquared(); + + if (delta >= 0.0) + { + const double timeP = (-factor + sqrt(delta)) / divisor; + const double timeM = (-factor - sqrt(delta)) / divisor; + + if (timeM > 0.0) + return timeM; + + return timeP; + } + + return -1.0; +} + +double ParabolaTrajectory::SearchFixedHeightMeetTime(CoordStruct* pSourceCrd, CoordStruct* pTargetCrd, CoordStruct* pOffsetCrd, double gravity) +{ + const double delta = 1e-5; + double meetTime = (this->ThrowHeight << 2) / gravity; + + for (int i = 0; i < 10; ++i) + { + const double differential = this->CheckFixedHeightEquation(pSourceCrd, pTargetCrd, pOffsetCrd, meetTime, gravity); + const double dDifferential = (this->CheckFixedHeightEquation(pSourceCrd, pTargetCrd, pOffsetCrd, (meetTime + delta), gravity) - differential) / delta; + + if (abs(dDifferential) < 1e-10) + return meetTime; + + const double difference = differential / dDifferential; + const double meetTimeNew = meetTime - difference; + + if (abs(difference) < 1.0) + return meetTimeNew; + + meetTime = meetTimeNew; + } + + return -1.0; +} + +double ParabolaTrajectory::CheckFixedHeightEquation(CoordStruct* pSourceCrd, CoordStruct* pTargetCrd, CoordStruct* pOffsetCrd, double meetTime, double gravity) +{ + const int meetHeight = static_cast((pTargetCrd->Z - this->LastTargetCoord.Z) * meetTime) + pTargetCrd->Z + pOffsetCrd->Z; + const int maxHeight = meetHeight > pSourceCrd->Z ? this->ThrowHeight + meetHeight : this->ThrowHeight + pSourceCrd->Z; + return sqrt((maxHeight - pSourceCrd->Z) * 2 / gravity) + sqrt((maxHeight - meetHeight) * 2 / gravity) - meetTime; +} + +double ParabolaTrajectory::SearchFixedAngleMeetTime(CoordStruct* pSourceCrd, CoordStruct* pTargetCrd, CoordStruct* pOffsetCrd, double radian, double gravity) +{ + const double delta = 1e-5; + double meetTime = 512 * sin(radian) / gravity; + + for (int i = 0; i < 10; ++i) + { + const double differential = this->CheckFixedAngleEquation(pSourceCrd, pTargetCrd, pOffsetCrd, meetTime, radian, gravity); + const double dDifferential = (this->CheckFixedAngleEquation(pSourceCrd, pTargetCrd, pOffsetCrd, (meetTime + delta), radian, gravity) - differential) / delta; + + if (abs(dDifferential) < 1e-10) + return meetTime; + + const double difference = differential / dDifferential; + const double meetTimeNew = meetTime - difference; + + if (abs(difference) < 1.0) + return meetTimeNew; + + meetTime = meetTimeNew; + } + + return -1.0; +} + +double ParabolaTrajectory::CheckFixedAngleEquation(CoordStruct* pSourceCrd, CoordStruct* pTargetCrd, CoordStruct* pOffsetCrd, double meetTime, double radian, double gravity) +{ + const CoordStruct distanceCoords = (*pTargetCrd - this->LastTargetCoord) * meetTime + *pTargetCrd + *pOffsetCrd - *pSourceCrd; + const double horizontalDistance = Point2D{ distanceCoords.X, distanceCoords.Y }.Magnitude(); + + const double horizontalVelocity = horizontalDistance / meetTime; + const double verticalVelocity = horizontalVelocity * tan(radian); + + const double upTime = verticalVelocity / gravity; + const double maxHeight = 0.5 * verticalVelocity * upTime; + const double downTime = sqrt(2 * (maxHeight - distanceCoords.Z) / gravity); + + return upTime + downTime - meetTime; +} + +bool ParabolaTrajectory::CalculateBulletVelocityAfterBounce(BulletClass* pBullet, CellClass* pCell, double gravity) +{ + --this->BounceTimes; + this->ShouldBounce = false; + + const BulletVelocity groundNormalVector = this->GetGroundNormalVector(pBullet, pCell); + pBullet->Velocity = (this->LastVelocity - groundNormalVector * (this->LastVelocity * groundNormalVector) * 2) * this->BounceCoefficient; + pBullet->Velocity.Z -= gravity; + + if (this->BounceDetonate) + { + TechnoClass* const pFirer = pBullet->Owner; + HouseClass* const pOwner = pFirer ? pFirer->Owner : BulletExt::ExtMap.Find(pBullet)->FirerHouse; + WarheadTypeExt::DetonateAt(pBullet->WH, pBullet->Location, pFirer, pBullet->Health, pOwner); + } + + if (const int damage = pBullet->Health) + { + if (const int newDamage = static_cast(damage * this->BounceAttenuation)) + pBullet->Health = newDamage; + else + pBullet->Health = damage > 0 ? 1 : -1; + } + + return false; +} + +BulletVelocity ParabolaTrajectory::GetGroundNormalVector(BulletClass* pBullet, CellClass* pCell) +{ + if (const unsigned char index = pCell->SlopeIndex) + { + Vector2D factor { 0.0, 0.0 }; + + if (index <= 4) + factor = Vector2D{ 0.3763770469559380854890894443664, 0.9264665771223091335116047861327 }; + else if (index <= 12) + factor = Vector2D{ 0.3522530794922131411764879370407, 0.8670845033654477321267395373309 }; + else + factor = Vector2D{ 0.5333964609104418418483761938761, 0.6564879518897745745826168540013 }; + + switch (index) + { + case 1: + return BulletVelocity{ -factor.X, 0.0, factor.Y }; + case 2: + return BulletVelocity{ 0.0, -factor.X, factor.Y }; + case 3: + return BulletVelocity{ factor.X, 0.0, factor.Y }; + case 4: + return BulletVelocity{ 0.0, factor.X, factor.Y }; + case 5: + case 9: + case 13: + return BulletVelocity{ -factor.X, -factor.X, factor.Y }; + case 6: + case 10: + case 14: + return BulletVelocity{ factor.X, -factor.X, factor.Y }; + case 7: + case 11: + case 15: + return BulletVelocity{ factor.X, factor.X, factor.Y }; + case 8: + case 12: + case 16: + return BulletVelocity{ -factor.X, factor.X, factor.Y }; + default: + return BulletVelocity{ 0.0, 0.0, 1.0 }; + } + } + + const double horizontalVelocity = Vector2D{ pBullet->Velocity.X, pBullet->Velocity.Y }.Magnitude(); + const BulletVelocity velocity = horizontalVelocity > 362.1 ? pBullet->Velocity * (362.1 / horizontalVelocity) : pBullet->Velocity; + const CoordStruct velocityCoords { static_cast(velocity.X), static_cast(velocity.Y), static_cast(velocity.Z) }; + + const int cellHeight = pCell->GetCoords().Z; + const int bulletHeight = pBullet->Location.Z; + const int lastCellHeight = MapClass::Instance->GetCellFloorHeight(pBullet->Location - velocityCoords); + + if (bulletHeight < cellHeight && (cellHeight - lastCellHeight) > 384) + { + CellStruct cell = pCell->MapCoords; + const short reverseSgnX = pBullet->Velocity.X >= 0.0 ? -1 : 1; + const short reverseSgnY = pBullet->Velocity.Y >= 0.0 ? -1 : 1; + int index = 0; + + if (this->CheckBulletHitCliff(cell.X + reverseSgnX, cell.Y, bulletHeight, lastCellHeight)) + { + if (!this->CheckBulletHitCliff(cell.X, cell.Y + reverseSgnY, bulletHeight, lastCellHeight)) + { + if (!this->CheckBulletHitCliff(cell.X - reverseSgnX, cell.Y, bulletHeight, lastCellHeight)) + return BulletVelocity{ 0.0, static_cast(reverseSgnY), 0.0 }; + + index = 2; + } + } + else + { + if (this->CheckBulletHitCliff(cell.X + reverseSgnX, cell.Y - reverseSgnY, bulletHeight, lastCellHeight)) + { + if (this->CheckBulletHitCliff(cell.X, cell.Y + reverseSgnY, bulletHeight, lastCellHeight)) + index = 1; + else if (!this->CheckBulletHitCliff(cell.X - reverseSgnX, cell.Y + reverseSgnY, bulletHeight, lastCellHeight)) + index = 2; + } + else + { + if (this->CheckBulletHitCliff(cell.X, cell.Y + reverseSgnY, bulletHeight, lastCellHeight)) + return BulletVelocity{ static_cast(reverseSgnX), 0.0, 0.0 }; + else if (this->CheckBulletHitCliff(cell.X - reverseSgnX, cell.Y + reverseSgnY, bulletHeight, lastCellHeight)) + index = 1; + } + } + + if (index == 1) + return BulletVelocity{ 0.8944271909999158785636694674925 * reverseSgnX, 0.4472135954999579392818347337463 * reverseSgnY, 0.0 }; + else if (index == 2) + return BulletVelocity{ 0.4472135954999579392818347337463 * reverseSgnX, 0.8944271909999158785636694674925 * reverseSgnY, 0.0 }; + + return BulletVelocity{ 0.7071067811865475244008443621049 * reverseSgnX, 0.7071067811865475244008443621049 * reverseSgnY, 0.0 }; + } + + return BulletVelocity{ 0.0, 0.0, 1.0 }; +} + +bool ParabolaTrajectory::CheckBulletHitCliff(short X, short Y, int bulletHeight, int lastCellHeight) +{ + if (CellClass* const pCell = MapClass::Instance->TryGetCellAt(CellStruct{ X, Y })) + { + const int cellHeight = pCell->GetCoords().Z; + + if (bulletHeight < cellHeight && (cellHeight - lastCellHeight) > 384) + return true; + } + + return false; +} + +bool ParabolaTrajectory::BulletDetonateLastCheck(BulletClass* pBullet, double gravity) +{ + pBullet->Velocity.Z -= gravity; + + const CoordStruct velocityCoords { static_cast(pBullet->Velocity.X), static_cast(pBullet->Velocity.Y), static_cast(pBullet->Velocity.Z) }; + const CoordStruct futureCoords = pBullet->Location + velocityCoords; + + if (this->NeedExtraCheck) + { + const CellStruct sourceCell = CellClass::Coord2Cell(pBullet->Location); + const CellStruct targetCell = CellClass::Coord2Cell(futureCoords); + const CellStruct cellDist = sourceCell - targetCell; + const CellStruct cellPace = CellStruct { static_cast(std::abs(cellDist.X)), static_cast(std::abs(cellDist.Y)) }; + + const size_t largePace = static_cast(std::max(cellPace.X, cellPace.Y)); + const CoordStruct stepCoord = largePace ? velocityCoords * (1.0 / largePace) : CoordStruct::Empty; + CoordStruct curCoord = pBullet->Location; + CellClass* pCurCell = MapClass::Instance->GetCellAt(sourceCell); + + for (size_t i = 0; i < largePace; ++i) + { + const int cellHeight = MapClass::Instance->GetCellFloorHeight(curCoord); + + if (curCoord.Z < cellHeight) + { + this->LastVelocity = pBullet->Velocity; + const double heightMult = abs((pBullet->Location.Z - cellHeight) / pBullet->Velocity.Z); + const double speedMult = static_cast(i) / largePace; + this->BulletDetonateEffectuate(pBullet, (heightMult < speedMult ? heightMult : speedMult)); + break; + } + + if (pBullet->Type->SubjectToWalls && pCurCell->OverlayTypeIndex != -1 && OverlayTypeClass::Array->GetItem(pCurCell->OverlayTypeIndex)->Wall) + { + pBullet->Velocity *= static_cast(i) / largePace; + this->ShouldDetonate = true; + return false; + } + + curCoord += stepCoord; + pCurCell = MapClass::Instance->GetCellAt(curCoord); + } + } + else + { + const int cellHeight = MapClass::Instance->GetCellFloorHeight(futureCoords); + + if (cellHeight < futureCoords.Z) + return false; + + this->LastVelocity = pBullet->Velocity; + this->BulletDetonateEffectuate(pBullet, abs((pBullet->Location.Z - cellHeight) / pBullet->Velocity.Z)); + } + + return false; +} + +void ParabolaTrajectory::BulletDetonateEffectuate(BulletClass* pBullet, double velocityMult) +{ + if (velocityMult < 1.0) + pBullet->Velocity *= velocityMult; + + if (this->BounceTimes > 0) + this->ShouldBounce = true; + else + this->ShouldDetonate = true; +} diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h new file mode 100644 index 0000000000..4da1bca5fa --- /dev/null +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h @@ -0,0 +1,171 @@ +#pragma once + +#include "PhobosTrajectory.h" + +class ParabolaTrajectoryType final : public PhobosTrajectoryType +{ +public: + ParabolaTrajectoryType() : PhobosTrajectoryType(TrajectoryFlag::Parabola) + , DetonationDistance { Leptons(102) } + , TargetSnapDistance { Leptons(128) } + , OpenFireMode { 0 } + , ThrowHeight { 600 } + , LaunchAngle { 30.0 } + , LeadTimeCalculate { false } + , LeadTimeSimplify { false } + , LeadTimeMultiplier { 1.0 } + , BounceTimes { 0 } + , BounceOnWater { false } + , BounceDetonate { false } + , BounceAttenuation { 0.8 } + , BounceCoefficient { 0.8 } + , OffsetCoord { { 0, 0, 0 } } + , RotateCoord { 0 } + , MirrorCoord { true } + , UseDisperseBurst { false } + , AxisOfRotation { { 0, 0, 1 } } + {} + + virtual bool Load(PhobosStreamReader& Stm, bool RegisterForChange) override; + virtual bool Save(PhobosStreamWriter& Stm) const override; + virtual PhobosTrajectory* CreateInstance() const override; + virtual void Read(CCINIClass* const pINI, const char* pSection) override; + + Valueable DetonationDistance; + Valueable TargetSnapDistance; + Valueable OpenFireMode; + Valueable ThrowHeight; + Valueable LaunchAngle; + Valueable LeadTimeCalculate; + Valueable LeadTimeSimplify; + Valueable LeadTimeMultiplier; + Valueable BounceTimes; + Valueable BounceOnWater; + Valueable BounceDetonate; + Valueable BounceAttenuation; + Valueable BounceCoefficient; + Valueable OffsetCoord; + Valueable RotateCoord; + Valueable MirrorCoord; + Valueable UseDisperseBurst; + Valueable AxisOfRotation; + // The faster the projectile's speed, the worse the visual effect of bounce function. +}; + +class ParabolaTrajectory final : public PhobosTrajectory +{ +public: + ParabolaTrajectory() : PhobosTrajectory(TrajectoryFlag::Parabola) + , DetonationDistance { Leptons(102) } + , TargetSnapDistance { Leptons(128) } + , OpenFireMode { 0 } + , ThrowHeight { 600 } + , LaunchAngle { 30.0 } + , LeadTimeCalculate { false } + , LeadTimeSimplify { false } + , LeadTimeMultiplier { 1.0 } + , BounceTimes { 0 } + , BounceOnWater { false } + , BounceDetonate { false } + , BounceAttenuation { 0.8 } + , BounceCoefficient { 0.8 } + , OffsetCoord {} + , RotateCoord { 0 } + , MirrorCoord { true } + , UseDisperseBurst { false } + , AxisOfRotation {} + , ShouldDetonate { false } + , ShouldBounce { false } + , NeedExtraCheck { false } + , LastTargetCoord {} + , CurrentBurst { 0 } + , CountOfBurst { 0 } + , WaitOneFrame {} + , LastVelocity {} + {} + + ParabolaTrajectory(PhobosTrajectoryType const* pType) : PhobosTrajectory(TrajectoryFlag::Parabola) + , DetonationDistance { Leptons(102) } + , TargetSnapDistance { Leptons(128) } + , OpenFireMode { 0 } + , ThrowHeight { 600 } + , LaunchAngle { 30.0 } + , LeadTimeCalculate { false } + , LeadTimeSimplify { false } + , LeadTimeMultiplier { 1.0 } + , BounceTimes { 0 } + , BounceOnWater { false } + , BounceDetonate { false } + , BounceAttenuation { 0.8 } + , BounceCoefficient { 0.8 } + , OffsetCoord {} + , RotateCoord { 0 } + , MirrorCoord { true } + , UseDisperseBurst { false } + , AxisOfRotation {} + , ShouldDetonate { false } + , ShouldBounce { false } + , NeedExtraCheck { false } + , LastTargetCoord {} + , CurrentBurst { 0 } + , CountOfBurst { 0 } + , WaitOneFrame {} + , LastVelocity {} + {} + + virtual bool Load(PhobosStreamReader& Stm, bool RegisterForChange) override; + virtual bool Save(PhobosStreamWriter& Stm) const override; + + virtual void OnUnlimbo(BulletClass* pBullet, CoordStruct* pCoord, BulletVelocity* pVelocity) override; + virtual bool OnAI(BulletClass* pBullet) override; + virtual void OnAIPreDetonate(BulletClass* pBullet) override; + virtual void OnAIVelocity(BulletClass* pBullet, BulletVelocity* pSpeed, BulletVelocity* pPosition) override; + virtual TrajectoryCheckReturnType OnAITargetCoordCheck(BulletClass* pBullet) override; + virtual TrajectoryCheckReturnType OnAITechnoCheck(BulletClass* pBullet, TechnoClass* pTechno) override; + + Leptons DetonationDistance; + Leptons TargetSnapDistance; + int OpenFireMode; + int ThrowHeight; + double LaunchAngle; + bool LeadTimeCalculate; + bool LeadTimeSimplify; + double LeadTimeMultiplier; + int BounceTimes; + bool BounceOnWater; + bool BounceDetonate; + double BounceAttenuation; + double BounceCoefficient; + CoordStruct OffsetCoord; + int RotateCoord; + bool MirrorCoord; + bool UseDisperseBurst; + CoordStruct AxisOfRotation; + bool ShouldDetonate; + bool ShouldBounce; + bool NeedExtraCheck; + CoordStruct LastTargetCoord; + int CurrentBurst; + int CountOfBurst; + CDTimerClass WaitOneFrame; + BulletVelocity LastVelocity; + +private: + void PrepareForOpenFire(BulletClass* pBullet); + bool BulletPrepareCheck(BulletClass* pBullet); + void CalculateBulletVelocityRightNow(BulletClass* pBullet, CoordStruct* pSourceCoords, double gravity); + void CalculateBulletVelocityLeadTime(BulletClass* pBullet, CoordStruct* pSourceCoords, double gravity); + void CheckIfNeedExtraCheck(BulletClass* pBullet); + double SearchVelocity(double horizontalDistance, int distanceCoordsZ, double radian, double gravity); + double CheckVelocityEquation(double horizontalDistance, int distanceCoordsZ, double velocity, double radian, double gravity); + double SolveFixedSpeedMeetTime(CoordStruct* pSourceCrd, CoordStruct* pTargetCrd, CoordStruct* pOffsetCrd, double horizontalSpeed); + double SearchFixedHeightMeetTime(CoordStruct* pSourceCrd, CoordStruct* pTargetCrd, CoordStruct* pOffsetCrd, double gravity); + double CheckFixedHeightEquation(CoordStruct* pSourceCrd, CoordStruct* pTargetCrd, CoordStruct* pOffsetCrd, double meetTime, double gravity); + double SearchFixedAngleMeetTime(CoordStruct* pSourceCrd, CoordStruct* pTargetCrd, CoordStruct* pOffsetCrd, double radian, double gravity); + double CheckFixedAngleEquation(CoordStruct* pSourceCrd, CoordStruct* pTargetCrd, CoordStruct* pOffsetCrd, double meetTime, double radian, double gravity); + bool CalculateBulletVelocityAfterBounce(BulletClass* pBullet, CellClass* pCell, double gravity); + BulletVelocity GetGroundNormalVector(BulletClass* pBullet, CellClass* pCell); + bool CheckBulletHitCliff(short X, short Y, int bulletHeight, int lastCellHeight); + bool BulletDetonateLastCheck(BulletClass* pBullet, double gravity); + void BulletDetonateEffectuate(BulletClass* pBullet, double velocityMult); +}; From 1255ae68d256af8c9859d77380a56ec34d9c2220 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Wed, 4 Sep 2024 14:55:01 +0800 Subject: [PATCH 02/32] Docs --- CREDITS.md | 2 + docs/New-or-Enhanced-Logics.md | 67 ++++++++++++++++++++++++++++++++-- docs/Whats-New.md | 1 + 3 files changed, 66 insertions(+), 4 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index 5fa07e8307..41b0a64f81 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -353,6 +353,8 @@ This page lists all the individual contributions to the project by their author. - Re-enable the Veinhole Monster and Weeds from TS - Recreate the weed-charging of SWs like the TS Chemical Missile - Allow to change the speed of gas particles +- **CrimRecya** + - New Parabola trajectory - **Ollerus** - Build limit group enhancement - Customizable rocker amplitude diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 9f9e818b8e..234e3d7b12 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -68,7 +68,7 @@ This page describes all the engine features that are either new and introduced b - `AttachEffect.(Required|Disallowed)MinCounts & (Required|Disallowed)MaxCounts` can be used to set the minimum and maximum number of instances required / disallowed to be on the Techno for `Cumulative=true` types (ignored for other types) respectively. - `AttachEffect.IgnoreFromSameSource` can be set to true to ignore effects that have been attached by the firer of the weapon and its Warhead. - `AttachEffect.CheckOnFirer` is set to true makes it so that the required / disallowed attached effects are checked from the firer of the weapon instead of the target. - + In `rulesmd.ini`: ```ini [AttachEffectTypes] @@ -430,7 +430,7 @@ Shield.InheritStateOnReplace=false ; boolean - `CreateUnit.ConsiderPathfinding`, if set to true, will consider whether or not the cell where the animation is located is occupied by other objects or impassable to the vehicle being created and will attempt to find a nearby cell that is not. Otherwise the vehicle will be created at the animation's location despite these obstacles if possible. - `CreateUnit.SpawnAnim` can be used to play another animation at created unit's location after it has appeared. This animation has same owner and invoker as the parent animation. - `CreateUnit.SpawnHeight` can be set to override the animation's height when determining where to spawn the created unit. Has no effect if `CreateUnit.AlwaysSpawnOnGround` is set to true. - + In `artmd.ini`: ```ini [SOMEANIM] ; AnimationType @@ -660,7 +660,10 @@ Currently interceptor weapons with projectiles that do not have `Inviso=true` wi - Projectiles can now have customizable trajectories. - `Trajectory` should not be combined with original game's projectile trajectory logics (`Arcing`, `ROT`, `Vertical` or `Inviso`). Attempt to do so will result in the other logics being disabled and a warning being written to log file. - - Initial speed of the projectile is defined by `Trajectory.Speed`, which unlike `Speed` used by `ROT` > 0 projectiles is defined on projectile not weapon. + - The speed of the projectile is defined by `Trajectory.Speed`, which unlike `Speed` used by `ROT` > 0 projectiles is defined on projectile not weapon. + - In `Trajectory=Straight`, it refers to the whole distance speed of the projectile and it has no restrictions. + - In `Trajectory=Bombard`, it refers to the initial speed of the projectile and it has no restrictions. + - In `Trajectory=Parabola`, it refers to the horizontal velocity of the projectile and is only used for modes 0, 3, or 5 and it has no restrictions. In `rulesmd.ini`: ```ini @@ -679,7 +682,7 @@ Trajectory.Speed=100.0 ; floating point value - `Trajectory.Straight.PassThrough` enables special case logic where the projectile does not detonate in contact with the target but ínstead travels up to a distance defined by `Trajectory.Straight.DetonationDistance`. Note that the firing angle of the projectile is adjusted with this in mind, making it fire straight ahead if the target is on same elevation. In `rulesmd.ini`: -```ini +```ini [SOMEPROJECTILE] ; Projectile Trajectory=Straight ; Trajectory type Trajectory.Straight.DetonationDistance=0.4 ; floating point value @@ -699,6 +702,62 @@ Trajectory=Bombard ; Trajectory type Trajectory.Bombard.Height=0.0 ; double ``` +#### Parabola trajectory + +- As the name says, this is a completely reset `Arcing` with different enhanced functions. Without doubt, It supported linkage with `Trajectory=Disperse`. + - `Trajectory.Parabola.DetonationDistance` controls the maximum distance in cells from intended target (checked at start of each game frame, before the projectile moves) at which the projectile will be forced to detonate. Set to 0 to disable forced detonation. + - `Trajectory.Parabola.TargetSnapDistance` controls the maximum distance in cells from intended target the projectile can be at moment of detonation to make the projectile 'snap' on the intended target. Set to 0 to disable snapping. + - `Trajectory.Parabola.OpenFireMode` controls how should the projectile be launched. This has the following 6 modes. + - 0 - Automatic calculation mode with fixed horizontal velocity, using `Trajectory.Speed` and target coordinates as calculation conditions, i.e. the flight time of the projectile is permanently fixed. + - 1 - Automatic calculation mode with fixed maximum height, useing `Trajectory.Parabola.ThrowHeight` and target coordinates as calculation conditions, i.e. the detonation time of the projectile is relatively fixed. + - 2 - Automatic calculation mode with fixed fire angle, useing `Trajectory.Parabola.LaunchAngle` and target coordinates as calculation conditions. In this mode, the performance consumption is high, and may have no solution. It is not recommended to enable `SubjectToCliffs` or enable `AA` with a smaller `MinimumRange` when using this mode. + - 3 - Fixed horizontal velocity and maximum height mode, using `Trajectory.Speed` and `Trajectory.Parabola.ThrowHeight` as calculation conditions, i.e. the trajectory will only undergo altitude changes with the height of the target. + - 4 - Fixed maximum height and fire angle mode, using `Trajectory.Parabola.ThrowHeight` and `Trajectory.Parabola.LaunchAngle` as calculation conditions, i.e. the trajectory will change horizontally with the height of the target. + - 5 - Fixed horizontal velocity and fire angle mode, using `Trajectory.Speed` and `Trajectory.Parabola.LaunchAngle` as calculation conditions, i.e. the trajectory will be permanently fixed. + - `Trajectory.Parabola.ThrowHeight` controls the maximum height of the projectile and is only used for modes 1, 3, or 4. The specific height will be determined by taking the larger of the launch height and the target height then increasing this value. Non positive numbers are not supported. + - `Trajectory.Parabola.LaunchAngle` controls the fire angle of the projectile and is only used for modes 2, 4, or 5. Only supports -90.0~90.0 (Cannot use boundary values) in Mode 2 or 5, and 0.0~90.0 (Cannot use boundary values) in Mode 4. + - `Trajectory.Parabola.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.Parabola.LeadTimeSimplify` controls whether only perform simplified calculations when calculate the lead time. You can simply consider this as another calculation mode. + - `Trajectory.Parabola.LeadTimeMultiplier` is an additional lead time multiplier, it will affect the aiming position. You can use this to reduce the errors caused by target speed and target distance. + - `Trajectory.Parabola.BounceTimes` controls how many times can it bounce back when the projectile hits the ground or cliff. Be aware that excessive projectile speed may cause abnormal operation. And `Trajectory.Parabola.DetonationDistance` do not conflict with this and will take effect simultaneously. So if you want to explode the bullet only after the times of bounces is exhausted, you should set `Trajectory.Parabola.DetonationDistance` to a non positive value. + - `Trajectory.Parabola.BounceOnWater` controls whether it can bounce on the water surface. + - `Trajectory.Parabola.BounceDetonate` controls whether it detonates the warhead once extra during each bounce. + - `Trajectory.Parabola.BounceAttenuation` controls the attenuation coefficient of projectile bounce damage, that is, how many times the next damage after each bounce is the damage just caused. This will also affect the damage of the final detonation. + - `Trajectory.Parabola.BounceCoefficient` controls the attenuation coefficient of projectile bounce elasticity, that is, how many times the speed after each bounce is the speed before bouncing. + - `Trajectory.Parabola.OffsetCoord` controls the offsets of the target. Projectile will aim at this position to attack. It also supports `Inaccurate=yes` and `Trajectory.Parabola.LeadTimeCalculate=true` on this basis. + - `Trajectory.Parabola.RotateCoord` controls whether to rotate the projectile's firing direction within the angle bisector of `Trajectory.Parabola.OffsetCoord` according to the weapon's `Burst`. Set to 0 to disable this function. + - `Trajectory.Parabola.MirrorCoord` controls whether `Trajectory.Parabola.OffsetCoord` need to mirror the lateral value to adapt to the current burst index. At the same time, the rotation direction calculated by `Trajectory.Parabola.RotateCoord` will also be reversed, and the rotation angle between each adjacent projectile on each side will not change as a result. + - `Trajectory.Parabola.UseDisperseBurst` controls whether the calculation of `Trajectory.Parabola.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.Parabola.AxisOfRotation` controls the rotation axis when calculating `Trajectory.Parabola.RotateCoord`. The axis will rotates with the unit orientation or the vector that from target position to the source position. + +In `rulesmd.ini`: +```ini +Trajectory=Parabola ; Trajectory type +Trajectory.Parabola.DetonationDistance=0.4 ; floating point value +Trajectory.Parabola.TargetSnapDistance=0.5 ; floating point value +Trajectory.Parabola.OpenFireMode=0 ; integer - Six different modes +Trajectory.Parabola.ThrowHeight=600 ; integer +Trajectory.Parabola.LaunchAngle=30 ; floating point value +Trajectory.Parabola.LeadTimeCalculate=no ; boolean +Trajectory.Parabola.LeadTimeSimplify=no ; boolean +Trajectory.Parabola.LeadTimeMultiplier=1.0 ; floating point value +Trajectory.Parabola.BounceTimes=0 ; integer +Trajectory.Parabola.BounceOnWater=no ; boolean +Trajectory.Parabola.BounceDetonate=no ; boolean +Trajectory.Parabola.BounceAttenuation=0.8 ; floating point value +Trajectory.Parabola.BounceCoefficient=0.8 ; floating point value +Trajectory.Parabola.OffsetCoord=0,0,0 ; integer - Forward,Lateral,Height +Trajectory.Parabola.RotateCoord=0 ; floating point value +Trajectory.Parabola.MirrorCoord=yes ; boolean +Trajectory.Parabola.UseDisperseBurst=no ; boolean +Trajectory.Parabola.AxisOfRotation=0,0,1 ; integer - Forward,Lateral,Height +``` + +```{note} +- Compared to vanilla `Arcing`, this can also be used for aircrafts and airburst weapon. +- Certainly, `Gravity` can also affect the trajectory. +``` + ### Shrapnel enhancements ![image](_static/images/shrapnel.gif) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 21fb678592..b361c83e4a 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -449,6 +449,7 @@ New: - Custom object palettes for TerrainTypes (by Starkku) - Forbidding parallel AI queues for specific TechnoTypes (by Starkku) - Nonprovocative Warheads (by Starkku) +- New Parabola trajectory (by CrimRecya) - Option to restore `PowerSurplus` setting for AI (by Starkku) Vanilla fixes: From 2fb125fea2bf000db989835beeb5be4cdd64da55 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Wed, 4 Sep 2024 15:03:13 +0800 Subject: [PATCH 03/32] Others --- Phobos.vcxproj | 2 + src/Ext/Bullet/Hooks.cpp | 61 ++++++++++++++++--- .../Bullet/Trajectories/PhobosTrajectory.cpp | 19 +++++- .../Bullet/Trajectories/PhobosTrajectory.h | 1 + 4 files changed, 72 insertions(+), 11 deletions(-) diff --git a/Phobos.vcxproj b/Phobos.vcxproj index da0fa52603..5bd1538dca 100644 --- a/Phobos.vcxproj +++ b/Phobos.vcxproj @@ -38,6 +38,7 @@ + @@ -198,6 +199,7 @@ + diff --git a/src/Ext/Bullet/Hooks.cpp b/src/Ext/Bullet/Hooks.cpp index 016c0fc68c..664e9c3e3b 100644 --- a/src/Ext/Bullet/Hooks.cpp +++ b/src/Ext/Bullet/Hooks.cpp @@ -305,9 +305,11 @@ DEFINE_HOOK(0x467CCA, BulletClass_AI_TargetSnapChecks, 0x6) } else if (auto const pExt = BulletAITemp::ExtData) { - if (pExt->Trajectory) + if (auto const pTrajectory = pExt->Trajectory) { - if (pExt->Trajectory->Flag == TrajectoryFlag::Straight) + const TrajectoryFlag flag = pTrajectory->Flag; + + if (flag == TrajectoryFlag::Straight || flag == TrajectoryFlag::Parabola) { R->EAX(pThis->Type); return SkipChecks; @@ -336,10 +338,18 @@ DEFINE_HOOK(0x468E61, BulletClass_Explode_TargetSnapChecks1, 0x6) } else if (auto const pExt = BulletExt::ExtMap.Find(pThis)) { - if (pExt->Trajectory && pExt->Trajectory->Flag == TrajectoryFlag::Straight && !pExt->SnappedToTarget) + if (!pExt->SnappedToTarget) { - R->EAX(pThis->Type); - return SkipChecks; + if (auto const pTrajectory = pExt->Trajectory) + { + const TrajectoryFlag flag = pTrajectory->Flag; + + if (flag == TrajectoryFlag::Straight || flag == TrajectoryFlag::Parabola) + { + R->EAX(pThis->Type); + return SkipChecks; + } + } } } @@ -367,8 +377,18 @@ DEFINE_HOOK(0x468E9F, BulletClass_Explode_TargetSnapChecks2, 0x6) // Fixes issues with walls etc. if (auto const pExt = BulletExt::ExtMap.Find(pThis)) { - if (pExt->Trajectory && pExt->Trajectory->Flag == TrajectoryFlag::Straight && !pExt->SnappedToTarget) - return SkipSetCoordinate; + if (!pExt->SnappedToTarget) + { + if (auto const pTrajectory = pExt->Trajectory) + { + const TrajectoryFlag flag = pTrajectory->Flag; + + if (flag == TrajectoryFlag::Straight || flag == TrajectoryFlag::Parabola) + { + return SkipSetCoordinate; + } + } + } } return 0; @@ -382,8 +402,15 @@ DEFINE_HOOK(0x468D3F, BulletClass_ShouldExplode_AirTarget, 0x6) if (auto const pExt = BulletExt::ExtMap.Find(pThis)) { - if (pExt->Trajectory && pExt->Trajectory->Flag == TrajectoryFlag::Straight) - return SkipCheck; + if (auto const pTrajectory = pExt->Trajectory) + { + const TrajectoryFlag flag = pTrajectory->Flag; + + if (flag == TrajectoryFlag::Straight || flag == TrajectoryFlag::Parabola) + { + return SkipCheck; + } + } } return 0; @@ -448,3 +475,19 @@ DEFINE_HOOK(0x44D23C, BuildingClass_Mission_Missile_ArcingFix, 0x7) return 0; } + +// Vanilla inertia effect only for bullets with ROT=0 +DEFINE_HOOK(0x415F25, AircraftClass_Fire_TrajectorySkipInertiaEffect, 0x6) +{ + enum { SkipCheck = 0x4160BC }; + + GET(BulletClass*, pThis, ESI); + + if (auto const pExt = BulletExt::ExtMap.Find(pThis)) + { + if (pExt->Trajectory) + return SkipCheck; + } + + return 0; +} diff --git a/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp b/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp index 3241501e66..810420d0cd 100644 --- a/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp @@ -7,8 +7,9 @@ #include #include -#include "BombardTrajectory.h" #include "StraightTrajectory.h" +#include "BombardTrajectory.h" +#include "ParabolaTrajectory.h" bool PhobosTrajectoryType::Load(PhobosStreamReader& Stm, bool RegisterForChange) { @@ -34,6 +35,8 @@ void PhobosTrajectoryType::CreateType(PhobosTrajectoryType*& pType, CCINIClass* pNewType = DLLCreate(); else if (_stricmp(Phobos::readBuffer, "Bombard") == 0) pNewType = DLLCreate(); + else if (_stricmp(Phobos::readBuffer, "Parabola") == 0) + pNewType = DLLCreate(); else bUpdateType = false; @@ -65,6 +68,9 @@ PhobosTrajectoryType* PhobosTrajectoryType::LoadFromStream(PhobosStreamReader& S case TrajectoryFlag::Bombard: pType = DLLCreate(); break; + case TrajectoryFlag::Parabola: + pType = DLLCreate(); + break; default: return nullptr; } @@ -114,9 +120,15 @@ bool PhobosTrajectory::Save(PhobosStreamWriter& Stm) const double PhobosTrajectory::GetTrajectorySpeed(BulletClass* pBullet) const { if (auto const pBulletTypeExt = BulletTypeExt::ExtMap.Find(pBullet->Type)) - return pBulletTypeExt->Trajectory_Speed; + { + double StraightSpeed = pBulletTypeExt->Trajectory_Speed; + StraightSpeed = StraightSpeed > 0.001 ? StraightSpeed : 0.001 ; + return StraightSpeed; + } else + { return 100.0; + } } PhobosTrajectory* PhobosTrajectory::LoadFromStream(PhobosStreamReader& Stm) @@ -137,6 +149,9 @@ PhobosTrajectory* PhobosTrajectory::LoadFromStream(PhobosStreamReader& Stm) case TrajectoryFlag::Bombard: pTraj = DLLCreate(); break; + case TrajectoryFlag::Parabola: + pTraj = DLLCreate(); + break; default: return nullptr; } diff --git a/src/Ext/Bullet/Trajectories/PhobosTrajectory.h b/src/Ext/Bullet/Trajectories/PhobosTrajectory.h index 755fe51f4c..f66e0e2c7d 100644 --- a/src/Ext/Bullet/Trajectories/PhobosTrajectory.h +++ b/src/Ext/Bullet/Trajectories/PhobosTrajectory.h @@ -13,6 +13,7 @@ enum class TrajectoryFlag : int Invalid = -1, Straight = 0, Bombard = 1, + Parabola = 4 }; enum class TrajectoryCheckReturnType : int From 2fd854beabc251eac51f17523a7909e387f75129 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Wed, 4 Sep 2024 17:29:38 +0800 Subject: [PATCH 04/32] New function --- docs/New-or-Enhanced-Logics.md | 6 +- .../Trajectories/ParabolaTrajectory.cpp | 70 +++++++++++++++---- .../Bullet/Trajectories/ParabolaTrajectory.h | 11 +++ 3 files changed, 71 insertions(+), 16 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 234e3d7b12..f5c780eebc 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -715,10 +715,12 @@ Trajectory.Bombard.Height=0.0 ; double - 4 - Fixed maximum height and fire angle mode, using `Trajectory.Parabola.ThrowHeight` and `Trajectory.Parabola.LaunchAngle` as calculation conditions, i.e. the trajectory will change horizontally with the height of the target. - 5 - Fixed horizontal velocity and fire angle mode, using `Trajectory.Speed` and `Trajectory.Parabola.LaunchAngle` as calculation conditions, i.e. the trajectory will be permanently fixed. - `Trajectory.Parabola.ThrowHeight` controls the maximum height of the projectile and is only used for modes 1, 3, or 4. The specific height will be determined by taking the larger of the launch height and the target height then increasing this value. Non positive numbers are not supported. - - `Trajectory.Parabola.LaunchAngle` controls the fire angle of the projectile and is only used for modes 2, 4, or 5. Only supports -90.0~90.0 (Cannot use boundary values) in Mode 2 or 5, and 0.0~90.0 (Cannot use boundary values) in Mode 4. + - `Trajectory.Parabola.LaunchAngle` controls the fire angle of the projectile and is only used for modes 2, 4, or 5. Only supports -90.0 ~ 90.0 (Cannot use boundary values) in Mode 2 or 5, and 0.0 ~ 90.0 (Cannot use boundary values) in Mode 4. - `Trajectory.Parabola.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.Parabola.LeadTimeSimplify` controls whether only perform simplified calculations when calculate the lead time. You can simply consider this as another calculation mode. - `Trajectory.Parabola.LeadTimeMultiplier` is an additional lead time multiplier, it will affect the aiming position. You can use this to reduce the errors caused by target speed and target distance. + - `Trajectory.Parabola.DetonationAngle` controls when the angle between the projectile in the current velocity direction and the horizontal plane is less than this value, it will detonate prematurely. Taking effect when the value is at -90.0 ~ 90.0 (Cannot use boundary values). + - `Trajectory.Parabola.DetonationHeight` controls when the projectile is in a descending state and below the height of the launch position plus this value, it will detonate prematurely. Taking effect when it is set to non negative value. - `Trajectory.Parabola.BounceTimes` controls how many times can it bounce back when the projectile hits the ground or cliff. Be aware that excessive projectile speed may cause abnormal operation. And `Trajectory.Parabola.DetonationDistance` do not conflict with this and will take effect simultaneously. So if you want to explode the bullet only after the times of bounces is exhausted, you should set `Trajectory.Parabola.DetonationDistance` to a non positive value. - `Trajectory.Parabola.BounceOnWater` controls whether it can bounce on the water surface. - `Trajectory.Parabola.BounceDetonate` controls whether it detonates the warhead once extra during each bounce. @@ -741,6 +743,8 @@ Trajectory.Parabola.LaunchAngle=30 ; floating point value Trajectory.Parabola.LeadTimeCalculate=no ; boolean Trajectory.Parabola.LeadTimeSimplify=no ; boolean Trajectory.Parabola.LeadTimeMultiplier=1.0 ; floating point value +Trajectory.Parabola.DetonationAngle=-90.0 ; floating point value +Trajectory.Parabola.DetonationHeight=-1 ; integer Trajectory.Parabola.BounceTimes=0 ; integer Trajectory.Parabola.BounceOnWater=no ; boolean Trajectory.Parabola.BounceDetonate=no ; boolean diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp index b1bd2a0603..892ae59e83 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp @@ -18,6 +18,8 @@ bool ParabolaTrajectoryType::Load(PhobosStreamReader& Stm, bool RegisterForChang .Process(this->LeadTimeCalculate, false) .Process(this->LeadTimeSimplify, false) .Process(this->LeadTimeMultiplier, false) + .Process(this->DetonationAngle, false) + .Process(this->DetonationHeight, false) .Process(this->BounceTimes, false) .Process(this->BounceOnWater, false) .Process(this->BounceDetonate, false) @@ -46,6 +48,8 @@ bool ParabolaTrajectoryType::Save(PhobosStreamWriter& Stm) const .Process(this->LeadTimeCalculate) .Process(this->LeadTimeSimplify) .Process(this->LeadTimeMultiplier) + .Process(this->DetonationAngle) + .Process(this->DetonationHeight) .Process(this->BounceTimes) .Process(this->BounceOnWater) .Process(this->BounceDetonate) @@ -77,6 +81,8 @@ void ParabolaTrajectoryType::Read(CCINIClass* const pINI, const char* pSection) this->LeadTimeCalculate.Read(exINI, pSection, "Trajectory.Parabola.LeadTimeCalculate"); this->LeadTimeSimplify.Read(exINI, pSection, "Trajectory.Parabola.LeadTimeSimplify"); this->LeadTimeMultiplier.Read(exINI, pSection, "Trajectory.Parabola.LeadTimeMultiplier"); + this->DetonationAngle.Read(exINI, pSection, "Trajectory.Parabola.DetonationAngle"); + this->DetonationHeight.Read(exINI, pSection, "Trajectory.Parabola.DetonationHeight"); this->BounceTimes.Read(exINI, pSection, "Trajectory.Parabola.BounceTimes"); this->BounceOnWater.Read(exINI, pSection, "Trajectory.Parabola.BounceOnWater"); this->BounceDetonate.Read(exINI, pSection, "Trajectory.Parabola.BounceDetonate"); @@ -102,6 +108,8 @@ bool ParabolaTrajectory::Load(PhobosStreamReader& Stm, bool RegisterForChange) .Process(this->LeadTimeCalculate) .Process(this->LeadTimeSimplify) .Process(this->LeadTimeMultiplier) + .Process(this->DetonationAngle) + .Process(this->DetonationHeight) .Process(this->BounceTimes) .Process(this->BounceOnWater) .Process(this->BounceDetonate) @@ -138,6 +146,8 @@ bool ParabolaTrajectory::Save(PhobosStreamWriter& Stm) const .Process(this->LeadTimeCalculate) .Process(this->LeadTimeSimplify) .Process(this->LeadTimeMultiplier) + .Process(this->DetonationAngle) + .Process(this->DetonationHeight) .Process(this->BounceTimes) .Process(this->BounceOnWater) .Process(this->BounceDetonate) @@ -173,6 +183,8 @@ void ParabolaTrajectory::OnUnlimbo(BulletClass* pBullet, CoordStruct* pCoord, Bu this->LeadTimeCalculate = pType->LeadTimeCalculate; this->LeadTimeSimplify = pType->LeadTimeSimplify; this->LeadTimeMultiplier = pType->LeadTimeMultiplier; + this->DetonationAngle = pType->DetonationAngle; + this->DetonationHeight = pType->DetonationHeight; this->BounceTimes = pType->BounceTimes; this->BounceOnWater = pType->BounceOnWater; this->BounceDetonate = pType->BounceDetonate; @@ -214,7 +226,7 @@ bool ParabolaTrajectory::OnAI(BulletClass* pBullet) if (this->WaitOneFrame.IsTicking() && this->BulletPrepareCheck(pBullet)) return false; - if (this->ShouldDetonate || (this->DetonationDistance > 0 && pBullet->TargetCoords.DistanceFrom(pBullet->Location) < this->DetonationDistance)) + if (this->BulletDetonatePreCheck(pBullet)) return true; CellClass* const pCell = MapClass::Instance->TryGetCellAt(pBullet->Location); @@ -381,12 +393,12 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C { double radian = this->LaunchAngle * Math::Pi / 180.0; radian = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? (Math::HalfPi / 3) : radian; - const double factor = cos(radian); + const double factor = Math::cos(radian); if (abs(factor) < 1e-10) break; - const double mult = sin(2 * radian); + const double mult = Math::sin(2 * radian); const double velocity = abs(mult) > 1e-10 ? sqrt((Unsorted::LeptonsPerCell << 2) * gravity / mult) : 0.0; if (velocity < 1e-10) @@ -448,7 +460,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C const double horizontalDistance = Point2D{ destinationCoords.X, destinationCoords.Y }.Magnitude(); const double horizontalVelocity = horizontalDistance / meetTime; - pBullet->Velocity.Z = horizontalVelocity * tan(radian) + gravity / 2; + pBullet->Velocity.Z = horizontalVelocity * Math::tan(radian) + gravity / 2; this->CheckIfNeedExtraCheck(pBullet); return; } @@ -492,7 +504,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C radian = (radian >= Math::HalfPi || radian <= 0.0) ? (Math::HalfPi / 3) : radian; const double horizontalDistance = Point2D{ destinationCoords.X, destinationCoords.Y }.Magnitude(); - const double mult = (pBullet->Velocity.Z / tan(radian)) / horizontalDistance; + const double mult = (pBullet->Velocity.Z / Math::tan(radian)) / horizontalDistance; pBullet->Velocity.X = destinationCoords.X * mult; pBullet->Velocity.Y = destinationCoords.Y * mult; @@ -519,7 +531,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C double radian = this->LaunchAngle * Math::Pi / 180.0; radian = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? (Math::HalfPi / 3) : radian; - pBullet->Velocity.Z = horizontalVelocity * tan(radian) + gravity / 2; + pBullet->Velocity.Z = horizontalVelocity * Math::tan(radian) + gravity / 2; this->CheckIfNeedExtraCheck(pBullet); return; } @@ -579,9 +591,9 @@ void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, C { double radian = this->LaunchAngle * Math::Pi / 180.0; double velocity = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? 100.0 : this->SearchVelocity(horizontalDistance, distanceCoords.Z, radian, gravity); - pBullet->Velocity.Z = velocity * sin(radian); + pBullet->Velocity.Z = velocity * Math::sin(radian); - const double mult = velocity * cos(radian) / horizontalDistance; + const double mult = velocity * Math::cos(radian) / horizontalDistance; pBullet->Velocity.X = distanceCoords.X * mult; pBullet->Velocity.Y = distanceCoords.Y * mult; break; @@ -607,7 +619,7 @@ void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, C double radian = this->LaunchAngle * Math::Pi / 180.0; radian = (radian >= Math::HalfPi || radian <= 0.0) ? (Math::HalfPi / 3) : radian; - const double mult = (pBullet->Velocity.Z / tan(radian)) / horizontalDistance; + const double mult = (pBullet->Velocity.Z / Math::tan(radian)) / horizontalDistance; pBullet->Velocity.X = distanceCoords.X * mult; pBullet->Velocity.Y = distanceCoords.Y * mult; @@ -623,7 +635,7 @@ void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, C double radian = this->LaunchAngle * Math::Pi / 180.0; radian = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? (Math::HalfPi / 3) : radian; - pBullet->Velocity.Z = horizontalSpeed * tan(radian); + pBullet->Velocity.Z = horizontalSpeed * Math::tan(radian); break; } default: // Fixed horizontal speed and aim at the target @@ -663,7 +675,7 @@ void ParabolaTrajectory::CheckIfNeedExtraCheck(BulletClass* pBullet) double ParabolaTrajectory::SearchVelocity(double horizontalDistance, int distanceCoordsZ, double radian, double gravity) { - const double mult = sin(2 * radian); + const double mult = Math::sin(2 * radian); double velocity = abs(mult) > 1e-10 ? sqrt(horizontalDistance * gravity / mult) : 0.0; velocity += distanceCoordsZ / gravity; velocity = velocity > 10.0 ? velocity : 10.0; @@ -691,8 +703,8 @@ double ParabolaTrajectory::SearchVelocity(double horizontalDistance, int distanc double ParabolaTrajectory::CheckVelocityEquation(double horizontalDistance, int distanceCoordsZ, double velocity, double radian, double gravity) { - const double horizontalVelocity = velocity * cos(radian); - const double verticalVelocity = velocity * sin(radian); + const double horizontalVelocity = velocity * Math::cos(radian); + const double verticalVelocity = velocity * Math::sin(radian); const double upTime = verticalVelocity / gravity; const double maxHeight = 0.5 * verticalVelocity * upTime; @@ -759,7 +771,7 @@ double ParabolaTrajectory::CheckFixedHeightEquation(CoordStruct* pSourceCrd, Coo double ParabolaTrajectory::SearchFixedAngleMeetTime(CoordStruct* pSourceCrd, CoordStruct* pTargetCrd, CoordStruct* pOffsetCrd, double radian, double gravity) { const double delta = 1e-5; - double meetTime = 512 * sin(radian) / gravity; + double meetTime = 512 * Math::sin(radian) / gravity; for (int i = 0; i < 10; ++i) { @@ -787,7 +799,7 @@ double ParabolaTrajectory::CheckFixedAngleEquation(CoordStruct* pSourceCrd, Coor const double horizontalDistance = Point2D{ distanceCoords.X, distanceCoords.Y }.Magnitude(); const double horizontalVelocity = horizontalDistance / meetTime; - const double verticalVelocity = horizontalVelocity * tan(radian); + const double verticalVelocity = horizontalVelocity * Math::tan(radian); const double upTime = verticalVelocity / gravity; const double maxHeight = 0.5 * verticalVelocity * upTime; @@ -934,6 +946,34 @@ bool ParabolaTrajectory::CheckBulletHitCliff(short X, short Y, int bulletHeight, return false; } +bool ParabolaTrajectory::BulletDetonatePreCheck(BulletClass* pBullet) +{ + if (this->ShouldDetonate) + return true; + + if (this->DetonationHeight >= 0 && pBullet->Velocity.Z < 0.0 && (pBullet->Location.Z - pBullet->SourceCoords.Z) < this->DetonationHeight) + return true; + + if (!this->DetonationAngle) + { + if (pBullet->Velocity.Z < 0.0) + return true; + } + else if (abs(this->DetonationAngle) < 90.0) + { + if (const double horizontalVelocity = Vector2D{ pBullet->Velocity.X, pBullet->Velocity.Y }.Magnitude()) + { + if ((pBullet->Velocity.Z / horizontalVelocity) < Math::tan(this->DetonationAngle * Math::Pi / 180.0)) + return true; + } + } + + if (this->DetonationDistance > 0 && pBullet->TargetCoords.DistanceFrom(pBullet->Location) < this->DetonationDistance) + return true; + + return false; +} + bool ParabolaTrajectory::BulletDetonateLastCheck(BulletClass* pBullet, double gravity) { pBullet->Velocity.Z -= gravity; diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h index 4da1bca5fa..dabb62582c 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h @@ -14,6 +14,8 @@ class ParabolaTrajectoryType final : public PhobosTrajectoryType , LeadTimeCalculate { false } , LeadTimeSimplify { false } , LeadTimeMultiplier { 1.0 } + , DetonationAngle { -90.0 } + , DetonationHeight { -1 } , BounceTimes { 0 } , BounceOnWater { false } , BounceDetonate { false } @@ -39,6 +41,8 @@ class ParabolaTrajectoryType final : public PhobosTrajectoryType Valueable LeadTimeCalculate; Valueable LeadTimeSimplify; Valueable LeadTimeMultiplier; + Valueable DetonationAngle; + Valueable DetonationHeight; Valueable BounceTimes; Valueable BounceOnWater; Valueable BounceDetonate; @@ -64,6 +68,8 @@ class ParabolaTrajectory final : public PhobosTrajectory , LeadTimeCalculate { false } , LeadTimeSimplify { false } , LeadTimeMultiplier { 1.0 } + , DetonationAngle { -90.0 } + , DetonationHeight { -1 } , BounceTimes { 0 } , BounceOnWater { false } , BounceDetonate { false } @@ -93,6 +99,8 @@ class ParabolaTrajectory final : public PhobosTrajectory , LeadTimeCalculate { false } , LeadTimeSimplify { false } , LeadTimeMultiplier { 1.0 } + , DetonationAngle { -90.0 } + , DetonationHeight { -1 } , BounceTimes { 0 } , BounceOnWater { false } , BounceDetonate { false } @@ -131,6 +139,8 @@ class ParabolaTrajectory final : public PhobosTrajectory bool LeadTimeCalculate; bool LeadTimeSimplify; double LeadTimeMultiplier; + double DetonationAngle; + int DetonationHeight; int BounceTimes; bool BounceOnWater; bool BounceDetonate; @@ -166,6 +176,7 @@ class ParabolaTrajectory final : public PhobosTrajectory bool CalculateBulletVelocityAfterBounce(BulletClass* pBullet, CellClass* pCell, double gravity); BulletVelocity GetGroundNormalVector(BulletClass* pBullet, CellClass* pCell); bool CheckBulletHitCliff(short X, short Y, int bulletHeight, int lastCellHeight); + bool BulletDetonatePreCheck(BulletClass* pBullet); bool BulletDetonateLastCheck(BulletClass* pBullet, double gravity); void BulletDetonateEffectuate(BulletClass* pBullet, double velocityMult); }; From 8480e05c64f4abe9c1780fe915a698e6b90f4380 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Thu, 5 Sep 2024 16:49:29 +0800 Subject: [PATCH 05/32] Fix `double`, add notes, change enum and revert `trajectory.speed` --- .../Trajectories/ParabolaTrajectory.cpp | 292 ++++++++++++++---- .../Bullet/Trajectories/ParabolaTrajectory.h | 10 +- .../Bullet/Trajectories/PhobosTrajectory.cpp | 11 +- src/Utilities/Enum.h | 10 + src/Utilities/TemplateDef.h | 38 +++ 5 files changed, 296 insertions(+), 65 deletions(-) diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp index 892ae59e83..72fd9bf962 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp @@ -317,7 +317,7 @@ void ParabolaTrajectory::PrepareForOpenFire(BulletClass* pBullet) pBullet->TargetCoords = theTargetCoords; const double gravity = BulletTypeExt::GetAdjustedGravity(pBullet->Type); - if (gravity <= 0.0) + if (gravity <= 1e-10) { pBullet->Velocity = BulletVelocity::Empty; this->ShouldDetonate = true; @@ -329,7 +329,7 @@ void ParabolaTrajectory::PrepareForOpenFire(BulletClass* pBullet) else this->CalculateBulletVelocityRightNow(pBullet, &theSourceCoords, gravity); - if (!this->UseDisperseBurst && this->RotateCoord != 0 && this->CountOfBurst > 1) + if (!this->UseDisperseBurst && abs(this->RotateCoord) > 1e-10 && this->CountOfBurst > 1) { BulletVelocity rotationAxis { @@ -340,9 +340,9 @@ void ParabolaTrajectory::PrepareForOpenFire(BulletClass* pBullet) const double rotationAxisLengthSquared = rotationAxis.MagnitudeSquared(); - if (rotationAxisLengthSquared != 0) + if (abs(rotationAxisLengthSquared) > 1e-10) { - double extraRotate = 0; + double extraRotate = 0.0; rotationAxis *= 1 / sqrt(rotationAxisLengthSquared); if (this->MirrorCoord) @@ -376,198 +376,282 @@ bool ParabolaTrajectory::BulletPrepareCheck(BulletClass* pBullet) void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, CoordStruct* pSourceCoords, double gravity) { - if (this->LeadTimeSimplify) + if (this->LeadTimeSimplify) // Only simple guess, not exact solution { int leadTime = 0; - // Only simple guess, not exact solution + // Step 1: Guess the time of encounter between the projectile and the target based on known conditions + // Directly assume that the distance between the position where the projectile hits the target and the starting point is 4 grids switch (this->OpenFireMode) { - case 1: - case 4: + case ParabolaFireMode::Height: + case ParabolaFireMode::HeightAndAngle: { + // Assuming equal height leadTime = static_cast(sqrt((this->ThrowHeight << 1) / gravity) * 1.25); break; } - case 2: + case ParabolaFireMode::Angle: { double radian = this->LaunchAngle * Math::Pi / 180.0; radian = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? (Math::HalfPi / 3) : radian; const double factor = Math::cos(radian); + // Check if the angle is appropriate if (abs(factor) < 1e-10) break; const double mult = Math::sin(2 * radian); - const double velocity = abs(mult) > 1e-10 ? sqrt((Unsorted::LeptonsPerCell << 2) * gravity / mult) : 0.0; - if (velocity < 1e-10) + // Check if the angle is appropriate again + if (abs(mult) < 1e-10) break; + const double velocity = sqrt((Unsorted::LeptonsPerCell << 2) * gravity / mult); + + // Assuming equal height leadTime = static_cast((Unsorted::LeptonsPerCell << 2) / (velocity * factor)); break; } default: { + // Assuming equal height leadTime = static_cast((Unsorted::LeptonsPerCell << 2) / this->GetTrajectorySpeed(pBullet)); break; } } + // Step 2: Substitute the time into the calculation of the attack coordinates pBullet->TargetCoords += (pBullet->Target->GetCoords() - this->LastTargetCoord) * (this->LeadTimeMultiplier * leadTime); + + // Step 3: Calculate the parabolic starting point vector this->CalculateBulletVelocityRightNow(pBullet, pSourceCoords, gravity); return; } CoordStruct targetCoords = pBullet->Target->GetCoords(); CoordStruct offsetCoords = pBullet->TargetCoords - targetCoords; - const double speedFixMult = this->LeadTimeMultiplier * 0.75; // A coefficient that should not exist here normally, but even so, there are still errors + + // A coefficient that should not exist here normally, but even so, there are still errors + const double speedFixMult = this->LeadTimeMultiplier * 0.75; switch (this->OpenFireMode) { - case 1: // Fixed max height and aim at the target + case ParabolaFireMode::Height: // Fixed max height and aim at the target { + // Step 1: Using Newton Iteration Method to determine the time of encounter between the projectile and the target const double meetTime = this->SearchFixedHeightMeetTime(pSourceCoords, &targetCoords, &offsetCoords, gravity); + + // Step 2: Substitute the time into the calculation of the attack coordinates pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; - if (meetTime <= 0.0 || destinationCoords.Magnitude() <= 0.0) + // Step 3: Check if it is an unsolvable solution + if (meetTime <= 1e-10 || destinationCoords.Magnitude() <= 1e-10) break; + // Step 4: Calculate the horizontal component of the projectile velocity pBullet->Velocity.X = destinationCoords.X / meetTime; pBullet->Velocity.Y = destinationCoords.Y / meetTime; + // Step 5: Determine the maximum height that the projectile should reach const int sourceHeight = pSourceCoords->Z, targetHeight = sourceHeight + destinationCoords.Z; const int maxHeight = destinationCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; + + // Step 6: Calculate the vertical component of the projectile velocity pBullet->Velocity.Z = sqrt(2 * gravity * (maxHeight - sourceHeight)) + gravity / 2; + + // Step 7: Record whether it requires additional checks during the flight this->CheckIfNeedExtraCheck(pBullet); return; } - case 2: // Fixed fire angle and aim at the target + case ParabolaFireMode::Angle: // Fixed fire angle and aim at the target { + // Step 1: Read the appropriate fire angle double radian = this->LaunchAngle * Math::Pi / 180.0; radian = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? (Math::HalfPi / 3) : radian; + // Step 2: Using Newton Iteration Method to determine the time of encounter between the projectile and the target const double meetTime = this->SearchFixedAngleMeetTime(pSourceCoords, &targetCoords, &offsetCoords, radian, gravity); + + // Step 3: Substitute the time into the calculation of the attack coordinates pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; - if (meetTime <= 0.0 || destinationCoords.Magnitude() <= 0.0) + // Step 4: Check if it is an unsolvable solution + if (meetTime <= 1e-10 || destinationCoords.Magnitude() <= 1e-10) break; + // Step 5: Calculate each horizontal component of the projectile velocity pBullet->Velocity.X = destinationCoords.X / meetTime; pBullet->Velocity.Y = destinationCoords.Y / meetTime; + // Step 6: Calculate whole horizontal component of the projectile velocity const double horizontalDistance = Point2D{ destinationCoords.X, destinationCoords.Y }.Magnitude(); const double horizontalVelocity = horizontalDistance / meetTime; + + // Step 7: Calculate the vertical component of the projectile velocity pBullet->Velocity.Z = horizontalVelocity * Math::tan(radian) + gravity / 2; + + // Step 8: Record whether it requires additional checks during the flight this->CheckIfNeedExtraCheck(pBullet); return; } - case 3: // Fixed horizontal speed and fixed max height + case ParabolaFireMode::SpeedAndHeight: // Fixed horizontal speed and fixed max height { + // Step 1: Read the appropriate horizontal speed const double horizontalSpeed = this->GetTrajectorySpeed(pBullet); + // Step 2: Calculate the time when the projectile meets the target directly using horizontal velocity const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, horizontalSpeed); + + // Step 3: Substitute the time into the calculation of the attack coordinates pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; - if (meetTime <= 0.0 || destinationCoords.Magnitude() <= 0.0) + // Step 4: Check if it is an unsolvable solution + if (meetTime <= 1e-10 || destinationCoords.Magnitude() <= 1e-10) break; + // Step 5: Calculate the ratio of horizontal velocity to horizontal distance const double horizontalDistance = Point2D{ destinationCoords.X, destinationCoords.Y }.Magnitude(); - const double mult = horizontalDistance > 0.0 ? horizontalSpeed / horizontalDistance : 1.0; + const double mult = horizontalDistance > 1e-10 ? horizontalSpeed / horizontalDistance : 1.0; + // Step 6: Calculate the horizontal component of the projectile velocity pBullet->Velocity.X = destinationCoords.X * mult; pBullet->Velocity.Y = destinationCoords.Y * mult; + // Step 7: Determine the maximum height that the projectile should reach const int sourceHeight = pSourceCoords->Z, targetHeight = sourceHeight + destinationCoords.Z; const int maxHeight = destinationCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; + + // Step 8: Calculate the vertical component of the projectile velocity pBullet->Velocity.Z = sqrt(2 * gravity * (maxHeight - sourceHeight)) + gravity / 2; + + // Step 9: Record whether it requires additional checks during the flight this->CheckIfNeedExtraCheck(pBullet); return; } - case 4: // Fixed max height and fixed fire angle + case ParabolaFireMode::HeightAndAngle: // Fixed max height and fixed fire angle { + // Step 1: Using Newton Iteration Method to determine the time of encounter between the projectile and the target const double meetTime = this->SearchFixedHeightMeetTime(pSourceCoords, &targetCoords, &offsetCoords, gravity); + + // Step 2: Substitute the time into the calculation of the attack coordinates pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; - if (meetTime <= 0.0 || destinationCoords.Magnitude() <= 0.0) + // Step 3: Check if it is an unsolvable solution + if (meetTime <= 1e-10 || destinationCoords.Magnitude() <= 1e-10) break; + // Step 4: Determine the maximum height that the projectile should reach const int sourceHeight = pSourceCoords->Z, targetHeight = sourceHeight + destinationCoords.Z; const int maxHeight = destinationCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; + + // Step 5: Calculate the vertical component of the projectile velocity pBullet->Velocity.Z = sqrt(2 * gravity * (maxHeight - sourceHeight)) + gravity / 2; + // Step 6: Read the appropriate fire angle double radian = this->LaunchAngle * Math::Pi / 180.0; - radian = (radian >= Math::HalfPi || radian <= 0.0) ? (Math::HalfPi / 3) : radian; + radian = (radian >= Math::HalfPi || radian <= 1e-10) ? (Math::HalfPi / 3) : radian; + // Step 7: Calculate the ratio of horizontal velocity to horizontal distance const double horizontalDistance = Point2D{ destinationCoords.X, destinationCoords.Y }.Magnitude(); const double mult = (pBullet->Velocity.Z / Math::tan(radian)) / horizontalDistance; + // Step 8: Calculate the horizontal component of the projectile velocity pBullet->Velocity.X = destinationCoords.X * mult; pBullet->Velocity.Y = destinationCoords.Y * mult; + + // Step 9: Record whether it requires additional checks during the flight this->CheckIfNeedExtraCheck(pBullet); return; } - case 5: // Fixed horizontal speed and fixed fire angle + case ParabolaFireMode::SpeedAndAngle: // Fixed horizontal speed and fixed fire angle { + // Step 1: Read the appropriate horizontal speed const double horizontalSpeed = this->GetTrajectorySpeed(pBullet); + // Step 2: Calculate the time when the projectile meets the target directly using horizontal velocity const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, horizontalSpeed); + + // Step 3: Substitute the time into the calculation of the attack coordinates pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; - if (meetTime <= 0.0 || destinationCoords.Magnitude() <= 0.0) + // Step 4: Check if it is an unsolvable solution + if (meetTime <= 1e-10 || destinationCoords.Magnitude() <= 1e-10) break; + // Step 5: Calculate the ratio of horizontal velocity to horizontal distance const double horizontalDistance = Point2D{ destinationCoords.X, destinationCoords.Y }.Magnitude(); - const double mult = horizontalDistance > 0.0 ? horizontalSpeed / horizontalDistance : 1.0; + const double mult = horizontalDistance > 1e-10 ? horizontalSpeed / horizontalDistance : 1.0; + // Step 6: Calculate each horizontal component of the projectile velocity pBullet->Velocity.X = destinationCoords.X * mult; pBullet->Velocity.Y = destinationCoords.Y * mult; + + // Step 7: Calculate whole horizontal component of the projectile velocity const double horizontalVelocity = horizontalDistance * mult; + // Step 8: Read the appropriate fire angle double radian = this->LaunchAngle * Math::Pi / 180.0; radian = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? (Math::HalfPi / 3) : radian; + + // Step 9: Calculate the vertical component of the projectile velocity pBullet->Velocity.Z = horizontalVelocity * Math::tan(radian) + gravity / 2; + + // Step 10: Record whether it requires additional checks during the flight this->CheckIfNeedExtraCheck(pBullet); return; } default: // Fixed horizontal speed and aim at the target { + // Step 1: Read the appropriate horizontal speed const double horizontalSpeed = this->GetTrajectorySpeed(pBullet); + // Step 2: Calculate the time when the projectile meets the target directly using horizontal velocity const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, horizontalSpeed); + + // Step 3: Substitute the time into the calculation of the attack coordinates pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; - if (meetTime <= 0.0 || destinationCoords.Magnitude() <= 0.0) + // Step 4: Check if it is an unsolvable solution + if (meetTime <= 1e-10 || destinationCoords.Magnitude() <= 1e-10) break; + // Step 5: Calculate the ratio of horizontal velocity to horizontal distance const double horizontalDistance = Point2D{ destinationCoords.X, destinationCoords.Y }.Magnitude(); - const double mult = horizontalDistance > 0.0 ? horizontalSpeed / horizontalDistance : 1.0; + const double mult = horizontalDistance > 1e-10 ? horizontalSpeed / horizontalDistance : 1.0; + // Step 6: Calculate the projectile velocity pBullet->Velocity.X = destinationCoords.X * mult; pBullet->Velocity.Y = destinationCoords.Y * mult; pBullet->Velocity.Z = destinationCoords.Z * mult + (gravity * horizontalDistance) / (2 * horizontalSpeed) + gravity / 2; + + // Step 7: Record whether it requires additional checks during the flight this->CheckIfNeedExtraCheck(pBullet); return; } } + // Reset target position pBullet->TargetCoords = targetCoords + offsetCoords; + + // Substitute into the no lead time algorithm this->CalculateBulletVelocityRightNow(pBullet, pSourceCoords, gravity); } void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, CoordStruct* pSourceCoords, double gravity) { + // Calculate horizontal distance const CoordStruct distanceCoords = pBullet->TargetCoords - *pSourceCoords; const double distance = distanceCoords.Magnitude(); const double horizontalDistance = Point2D{ distanceCoords.X, distanceCoords.Y }.Magnitude(); - if (distance <= 0.0) + if (distance <= 1e-10) { pBullet->Velocity = BulletVelocity::Empty; this->ShouldDetonate = true; @@ -576,73 +660,112 @@ void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, C switch (this->OpenFireMode) { - case 1: // Fixed max height and aim at the target + case ParabolaFireMode::Height: // Fixed max height and aim at the target { + // Step 1: Determine the maximum height that the projectile should reach const int sourceHeight = pSourceCoords->Z, targetHeight = pBullet->TargetCoords.Z; const int maxHeight = distanceCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; + + // Step 2: Calculate the vertical component of the projectile velocity pBullet->Velocity.Z = sqrt(2 * gravity * (maxHeight - sourceHeight)); + // Step 3: Calculate the total time it takes for the projectile to meet the target using the heights of the ascending and descending phases const double meetTime = sqrt(2 * (maxHeight - sourceHeight) / gravity) + sqrt(2 * (maxHeight - targetHeight) / gravity); + + // Step 4: Calculate the horizontal component of the projectile velocity pBullet->Velocity.X = distanceCoords.X / meetTime; pBullet->Velocity.Y = distanceCoords.Y / meetTime; break; } - case 2: // Fixed fire angle and aim at the target + case ParabolaFireMode::Angle: // Fixed fire angle and aim at the target { + // Step 1: Read the appropriate fire angle double radian = this->LaunchAngle * Math::Pi / 180.0; + + // Step 2: Using Newton Iteration Method to determine the projectile velocity double velocity = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? 100.0 : this->SearchVelocity(horizontalDistance, distanceCoords.Z, radian, gravity); + + // Step 3: Calculate the vertical component of the projectile velocity pBullet->Velocity.Z = velocity * Math::sin(radian); + // Step 4: Calculate the ratio of horizontal velocity to horizontal distance const double mult = velocity * Math::cos(radian) / horizontalDistance; + + // Step 5: Calculate the horizontal component of the projectile velocity pBullet->Velocity.X = distanceCoords.X * mult; pBullet->Velocity.Y = distanceCoords.Y * mult; break; } - case 3: // Fixed horizontal speed and fixed max height + case ParabolaFireMode::SpeedAndHeight: // Fixed horizontal speed and fixed max height { + // Step 1: Determine the maximum height that the projectile should reach const int sourceHeight = pSourceCoords->Z, targetHeight = pBullet->TargetCoords.Z; const int maxHeight = distanceCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; + + // Step 2: Calculate the vertical component of the projectile velocity pBullet->Velocity.Z = sqrt(2 * gravity * (maxHeight - sourceHeight)); + // Step 3: Read the appropriate horizontal speed const double horizontalSpeed = this->GetTrajectorySpeed(pBullet); - const double mult = horizontalDistance > 0.0 ? horizontalSpeed / horizontalDistance : 1.0; + // Step 4: Calculate the ratio of horizontal velocity to horizontal distance + const double mult = horizontalDistance > 1e-10 ? horizontalSpeed / horizontalDistance : 1.0; + + // Step 5: Calculate the horizontal component of the projectile velocity pBullet->Velocity.X = distanceCoords.X * mult; pBullet->Velocity.Y = distanceCoords.Y * mult; break; } - case 4: // Fixed max height and fixed fire angle + case ParabolaFireMode::HeightAndAngle: // Fixed max height and fixed fire angle { + // Step 1: Determine the maximum height that the projectile should reach const int sourceHeight = pSourceCoords->Z, targetHeight = pBullet->TargetCoords.Z; const int maxHeight = distanceCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; + + // Step 2: Calculate the vertical component of the projectile velocity pBullet->Velocity.Z = sqrt(2 * gravity * (maxHeight - sourceHeight)); + // Step 3: Read the appropriate fire angle double radian = this->LaunchAngle * Math::Pi / 180.0; - radian = (radian >= Math::HalfPi || radian <= 0.0) ? (Math::HalfPi / 3) : radian; + radian = (radian >= Math::HalfPi || radian <= 1e-10) ? (Math::HalfPi / 3) : radian; + + // Step 4: Calculate the ratio of horizontal velocity to horizontal distance const double mult = (pBullet->Velocity.Z / Math::tan(radian)) / horizontalDistance; + // Step 5: Calculate the horizontal component of the projectile velocity pBullet->Velocity.X = distanceCoords.X * mult; pBullet->Velocity.Y = distanceCoords.Y * mult; break; } - case 5: // Fixed horizontal speed and fixed fire angle + case ParabolaFireMode::SpeedAndAngle: // Fixed horizontal speed and fixed fire angle { + // Step 1: Read the appropriate horizontal speed const double horizontalSpeed = this->GetTrajectorySpeed(pBullet); - const double mult = horizontalDistance > 0.0 ? horizontalSpeed / horizontalDistance : 1.0; + // Step 2: Calculate the ratio of horizontal velocity to horizontal distance + const double mult = horizontalDistance > 1e-10 ? horizontalSpeed / horizontalDistance : 1.0; + + // Step 3: Calculate the horizontal component of the projectile velocity pBullet->Velocity.X = distanceCoords.X * mult; pBullet->Velocity.Y = distanceCoords.Y * mult; + // Step 4: Read the appropriate fire angle double radian = this->LaunchAngle * Math::Pi / 180.0; radian = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? (Math::HalfPi / 3) : radian; + + // Step 5: Calculate the vertical component of the projectile velocity pBullet->Velocity.Z = horizontalSpeed * Math::tan(radian); break; } default: // Fixed horizontal speed and aim at the target { + // Step 1: Read the appropriate horizontal speed const double horizontalSpeed = this->GetTrajectorySpeed(pBullet); - const double mult = horizontalDistance > 0.0 ? horizontalSpeed / horizontalDistance : 1.0; + // Step 2: Calculate the ratio of horizontal velocity to horizontal distance + const double mult = horizontalDistance > 1e-10 ? horizontalSpeed / horizontalDistance : 1.0; + + // Step 3: Calculate the projectile velocity pBullet->Velocity.X = distanceCoords.X * mult; pBullet->Velocity.Y = distanceCoords.Y * mult; pBullet->Velocity.Z = distanceCoords.Z * mult + (gravity * horizontalDistance) / (2 * horizontalSpeed); @@ -650,17 +773,20 @@ void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, C } } + // Record whether it requires additional checks during the flight this->CheckIfNeedExtraCheck(pBullet); - pBullet->Velocity.Z += gravity / 2; // Offset the gravity effect of the first time update + + // Offset the gravity effect of the first time update + pBullet->Velocity.Z += gravity / 2; } void ParabolaTrajectory::CheckIfNeedExtraCheck(BulletClass* pBullet) { switch (this->OpenFireMode) { - case 1: // Fixed max height and aim at the target - case 2: // Fixed fire angle and aim at the target - case 4: // Fixed max height and fixed fire angle + case ParabolaFireMode::Height: // Fixed max height and aim at the target + case ParabolaFireMode::Angle: // Fixed fire angle and aim at the target + case ParabolaFireMode::HeightAndAngle: // Fixed max height and fixed fire angle { this->NeedExtraCheck = Vector2D{ pBullet->Velocity.X, pBullet->Velocity.Y }.MagnitudeSquared() > 65536.0; break; @@ -675,59 +801,84 @@ void ParabolaTrajectory::CheckIfNeedExtraCheck(BulletClass* pBullet) double ParabolaTrajectory::SearchVelocity(double horizontalDistance, int distanceCoordsZ, double radian, double gravity) { + // Estimate initial velocity const double mult = Math::sin(2 * radian); double velocity = abs(mult) > 1e-10 ? sqrt(horizontalDistance * gravity / mult) : 0.0; velocity += distanceCoordsZ / gravity; velocity = velocity > 10.0 ? velocity : 10.0; + + // Step size const double delta = 1e-6; - for (int i = 0; i < 10; ++i) // Newton iteration method + // Newton Iteration Method + for (int i = 0; i < 10; ++i) { + // Substitute into the estimate speed const double differential = this->CheckVelocityEquation(horizontalDistance, distanceCoordsZ, velocity, radian, gravity); const double dDifferential = (this->CheckVelocityEquation(horizontalDistance, distanceCoordsZ, (velocity + delta), radian, gravity) - differential) / delta; - if (abs(dDifferential) < 1e-10) // Unacceptable divisor + // Check unacceptable divisor + if (abs(dDifferential) < 1e-10) return velocity; + // Calculate the speed of the next iteration const double difference = differential / dDifferential; const double velocityNew = velocity - difference; - if (abs(difference) < 8.0) // Tolerable error + // Check tolerable error + if (abs(difference) < 8.0) return velocityNew; + // Update the speed velocity = velocityNew; } - return 10.0; // Unsolvable + // Unsolvable + return 10.0; } double ParabolaTrajectory::CheckVelocityEquation(double horizontalDistance, int distanceCoordsZ, double velocity, double radian, double gravity) { + // Calculate each component of the projectile velocity const double horizontalVelocity = velocity * Math::cos(radian); const double verticalVelocity = velocity * Math::sin(radian); + // Calculate the time of the rising phase const double upTime = verticalVelocity / gravity; + + // Calculate the maximum height that the projectile can reach const double maxHeight = 0.5 * verticalVelocity * upTime; + + // Calculate the time of the descent phase const double downTime = sqrt(2 * (maxHeight - distanceCoordsZ) / gravity); + + // Calculate the total time required for horizontal movement const double wholeTime = horizontalDistance / horizontalVelocity; + // Calculate the difference between the total vertical motion time and the total horizontal motion time return wholeTime - (upTime + downTime); } double ParabolaTrajectory::SolveFixedSpeedMeetTime(CoordStruct* pSourceCrd, CoordStruct* pTargetCrd, CoordStruct* pOffsetCrd, double horizontalSpeed) { + // Project all conditions onto a horizontal plane const Point2D targetSpeedCrd { pTargetCrd->X - this->LastTargetCoord.X, pTargetCrd->Y - this->LastTargetCoord.Y }; const Point2D destinationCrd { pTargetCrd->X + pOffsetCrd->X - pSourceCrd->X, pTargetCrd->Y + pOffsetCrd->Y - pSourceCrd->Y }; + + // Establishing a quadratic equation using time as a variable: + // (destinationCrd + targetSpeedCrd * time).Magnitude() = horizontalSpeed * time + + // Solve this quadratic equation const double divisor = (targetSpeedCrd.MagnitudeSquared() - horizontalSpeed * horizontalSpeed) * 2; const double factor = 2 * (targetSpeedCrd * destinationCrd); const double delta = factor * factor - 2 * divisor * destinationCrd.MagnitudeSquared(); - if (delta >= 0.0) + if (delta >= 1e-10) { const double timeP = (-factor + sqrt(delta)) / divisor; const double timeM = (-factor - sqrt(delta)) / divisor; - if (timeM > 0.0) + if (timeM > 1e-10) return timeM; return timeP; @@ -738,6 +889,7 @@ double ParabolaTrajectory::SolveFixedSpeedMeetTime(CoordStruct* pSourceCrd, Coor double ParabolaTrajectory::SearchFixedHeightMeetTime(CoordStruct* pSourceCrd, CoordStruct* pTargetCrd, CoordStruct* pOffsetCrd, double gravity) { + // Similar to method SearchVelocity, no further elaboration will be provided const double delta = 1e-5; double meetTime = (this->ThrowHeight << 2) / gravity; @@ -763,13 +915,19 @@ double ParabolaTrajectory::SearchFixedHeightMeetTime(CoordStruct* pSourceCrd, Co double ParabolaTrajectory::CheckFixedHeightEquation(CoordStruct* pSourceCrd, CoordStruct* pTargetCrd, CoordStruct* pOffsetCrd, double meetTime, double gravity) { + // Calculate how high the target will reach during this period of time const int meetHeight = static_cast((pTargetCrd->Z - this->LastTargetCoord.Z) * meetTime) + pTargetCrd->Z + pOffsetCrd->Z; + + // Calculate how high the projectile can fly during this period of time const int maxHeight = meetHeight > pSourceCrd->Z ? this->ThrowHeight + meetHeight : this->ThrowHeight + pSourceCrd->Z; + + // Calculate the difference between these two times return sqrt((maxHeight - pSourceCrd->Z) * 2 / gravity) + sqrt((maxHeight - meetHeight) * 2 / gravity) - meetTime; } double ParabolaTrajectory::SearchFixedAngleMeetTime(CoordStruct* pSourceCrd, CoordStruct* pTargetCrd, CoordStruct* pOffsetCrd, double radian, double gravity) { + // Similar to method SearchVelocity, no further elaboration will be provided const double delta = 1e-5; double meetTime = 512 * Math::sin(radian) / gravity; @@ -795,16 +953,28 @@ double ParabolaTrajectory::SearchFixedAngleMeetTime(CoordStruct* pSourceCrd, Coo double ParabolaTrajectory::CheckFixedAngleEquation(CoordStruct* pSourceCrd, CoordStruct* pTargetCrd, CoordStruct* pOffsetCrd, double meetTime, double radian, double gravity) { + // Using the estimated time to obtain the predicted location of the target const CoordStruct distanceCoords = (*pTargetCrd - this->LastTargetCoord) * meetTime + *pTargetCrd + *pOffsetCrd - *pSourceCrd; + + // Calculate the horizontal distance between the target and the calculation const double horizontalDistance = Point2D{ distanceCoords.X, distanceCoords.Y }.Magnitude(); + // Calculate the horizontal velocity const double horizontalVelocity = horizontalDistance / meetTime; + + // Calculate the vertical velocity const double verticalVelocity = horizontalVelocity * Math::tan(radian); + // Calculate the time of the rising phase const double upTime = verticalVelocity / gravity; + + // Calculate the maximum height that the projectile can reach const double maxHeight = 0.5 * verticalVelocity * upTime; + + // Calculate the time of the descent phase const double downTime = sqrt(2 * (maxHeight - distanceCoords.Z) / gravity); + // Calculate the difference between the actual flight time of the projectile obtained and the initially estimated time return upTime + downTime - meetTime; } @@ -841,6 +1011,12 @@ BulletVelocity ParabolaTrajectory::GetGroundNormalVector(BulletClass* pBullet, C { Vector2D factor { 0.0, 0.0 }; + // 0.3763770469559380854890894443664 -> Unsorted::LevelHeight / sqrt(Unsorted::LevelHeight * Unsorted::LevelHeight + Unsorted::LeptonsPerCell * Unsorted::LeptonsPerCell) + // 0.9264665771223091335116047861327 -> Unsorted::LeptonsPerCell / sqrt(Unsorted::LevelHeight * Unsorted::LevelHeight + Unsorted::LeptonsPerCell * Unsorted::LeptonsPerCell) + // 0.3522530794922131411764879370407 -> Unsorted::LevelHeight / sqrt(2 * Unsorted::LevelHeight * Unsorted::LevelHeight + Unsorted::LeptonsPerCell * Unsorted::LeptonsPerCell) + // 0.8670845033654477321267395373309 -> Unsorted::LeptonsPerCell / sqrt(2 * Unsorted::LevelHeight * Unsorted::LevelHeight + Unsorted::LeptonsPerCell * Unsorted::LeptonsPerCell) + // 0.5333964609104418418483761938761 -> Unsorted::CellHeight / sqrt(2 * Unsorted::CellHeight * Unsorted::CellHeight + Unsorted::LeptonsPerCell * Unsorted::LeptonsPerCell) + // 0.6564879518897745745826168540013 -> Unsorted::LeptonsPerCell / sqrt(2 * Unsorted::CellHeight * Unsorted::CellHeight + Unsorted::LeptonsPerCell * Unsorted::LeptonsPerCell) if (index <= 4) factor = Vector2D{ 0.3763770469559380854890894443664, 0.9264665771223091335116047861327 }; else if (index <= 12) @@ -879,6 +1055,7 @@ BulletVelocity ParabolaTrajectory::GetGroundNormalVector(BulletClass* pBullet, C } } + // 362.1 -> Unsorted::LeptonsPerCell * sqrt(2) const double horizontalVelocity = Vector2D{ pBullet->Velocity.X, pBullet->Velocity.Y }.Magnitude(); const BulletVelocity velocity = horizontalVelocity > 362.1 ? pBullet->Velocity * (362.1 / horizontalVelocity) : pBullet->Velocity; const CoordStruct velocityCoords { static_cast(velocity.X), static_cast(velocity.Y), static_cast(velocity.Z) }; @@ -890,8 +1067,8 @@ BulletVelocity ParabolaTrajectory::GetGroundNormalVector(BulletClass* pBullet, C if (bulletHeight < cellHeight && (cellHeight - lastCellHeight) > 384) { CellStruct cell = pCell->MapCoords; - const short reverseSgnX = pBullet->Velocity.X >= 0.0 ? -1 : 1; - const short reverseSgnY = pBullet->Velocity.Y >= 0.0 ? -1 : 1; + const short reverseSgnX = pBullet->Velocity.X > -(1e-10) ? -1 : 1; + const short reverseSgnY = pBullet->Velocity.Y > -(1e-10) ? -1 : 1; int index = 0; if (this->CheckBulletHitCliff(cell.X + reverseSgnX, cell.Y, bulletHeight, lastCellHeight)) @@ -922,11 +1099,14 @@ BulletVelocity ParabolaTrajectory::GetGroundNormalVector(BulletClass* pBullet, C } } + // 0.4472135954999579392818347337463 -> 1 / sqrt(5) + // 0.8944271909999158785636694674925 -> 2 / sqrt(5) if (index == 1) return BulletVelocity{ 0.8944271909999158785636694674925 * reverseSgnX, 0.4472135954999579392818347337463 * reverseSgnY, 0.0 }; else if (index == 2) return BulletVelocity{ 0.4472135954999579392818347337463 * reverseSgnX, 0.8944271909999158785636694674925 * reverseSgnY, 0.0 }; + // 0.7071067811865475244008443621049 -> 1 / sqrt(2) return BulletVelocity{ 0.7071067811865475244008443621049 * reverseSgnX, 0.7071067811865475244008443621049 * reverseSgnY, 0.0 }; } @@ -951,21 +1131,27 @@ bool ParabolaTrajectory::BulletDetonatePreCheck(BulletClass* pBullet) if (this->ShouldDetonate) return true; - if (this->DetonationHeight >= 0 && pBullet->Velocity.Z < 0.0 && (pBullet->Location.Z - pBullet->SourceCoords.Z) < this->DetonationHeight) + if (this->DetonationHeight >= 0 && pBullet->Velocity.Z < 1e-10 && (pBullet->Location.Z - pBullet->SourceCoords.Z) < this->DetonationHeight) return true; - if (!this->DetonationAngle) + if (abs(this->DetonationAngle) < 1e-10) { - if (pBullet->Velocity.Z < 0.0) + if (pBullet->Velocity.Z < 1e-10) return true; } else if (abs(this->DetonationAngle) < 90.0) { - if (const double horizontalVelocity = Vector2D{ pBullet->Velocity.X, pBullet->Velocity.Y }.Magnitude()) + const double horizontalVelocity = Vector2D{ pBullet->Velocity.X, pBullet->Velocity.Y }.Magnitude(); + + if (horizontalVelocity > 1e-10) { if ((pBullet->Velocity.Z / horizontalVelocity) < Math::tan(this->DetonationAngle * Math::Pi / 180.0)) return true; } + else if (this->DetonationAngle > 1e-10 || pBullet->Velocity.Z < 1e-10) + { + return true; + } } if (this->DetonationDistance > 0 && pBullet->TargetCoords.DistanceFrom(pBullet->Location) < this->DetonationDistance) diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h index dabb62582c..c49325f3ae 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h @@ -8,7 +8,7 @@ class ParabolaTrajectoryType final : public PhobosTrajectoryType ParabolaTrajectoryType() : PhobosTrajectoryType(TrajectoryFlag::Parabola) , DetonationDistance { Leptons(102) } , TargetSnapDistance { Leptons(128) } - , OpenFireMode { 0 } + , OpenFireMode { ParabolaFireMode::Speed } , ThrowHeight { 600 } , LaunchAngle { 30.0 } , LeadTimeCalculate { false } @@ -35,7 +35,7 @@ class ParabolaTrajectoryType final : public PhobosTrajectoryType Valueable DetonationDistance; Valueable TargetSnapDistance; - Valueable OpenFireMode; + Valueable OpenFireMode; Valueable ThrowHeight; Valueable LaunchAngle; Valueable LeadTimeCalculate; @@ -62,7 +62,7 @@ class ParabolaTrajectory final : public PhobosTrajectory ParabolaTrajectory() : PhobosTrajectory(TrajectoryFlag::Parabola) , DetonationDistance { Leptons(102) } , TargetSnapDistance { Leptons(128) } - , OpenFireMode { 0 } + , OpenFireMode { ParabolaFireMode::Speed } , ThrowHeight { 600 } , LaunchAngle { 30.0 } , LeadTimeCalculate { false } @@ -93,7 +93,7 @@ class ParabolaTrajectory final : public PhobosTrajectory ParabolaTrajectory(PhobosTrajectoryType const* pType) : PhobosTrajectory(TrajectoryFlag::Parabola) , DetonationDistance { Leptons(102) } , TargetSnapDistance { Leptons(128) } - , OpenFireMode { 0 } + , OpenFireMode { ParabolaFireMode::Speed } , ThrowHeight { 600 } , LaunchAngle { 30.0 } , LeadTimeCalculate { false } @@ -133,7 +133,7 @@ class ParabolaTrajectory final : public PhobosTrajectory Leptons DetonationDistance; Leptons TargetSnapDistance; - int OpenFireMode; + ParabolaFireMode OpenFireMode; int ThrowHeight; double LaunchAngle; bool LeadTimeCalculate; diff --git a/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp b/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp index 810420d0cd..c07e28f9e4 100644 --- a/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp @@ -121,14 +121,11 @@ double PhobosTrajectory::GetTrajectorySpeed(BulletClass* pBullet) const { if (auto const pBulletTypeExt = BulletTypeExt::ExtMap.Find(pBullet->Type)) { - double StraightSpeed = pBulletTypeExt->Trajectory_Speed; - StraightSpeed = StraightSpeed > 0.001 ? StraightSpeed : 0.001 ; - return StraightSpeed; - } - else - { - return 100.0; + const double trajectorySpeed = pBulletTypeExt->Trajectory_Speed; + return abs(trajectorySpeed) > 1e-10 ? trajectorySpeed : 0.001; } + + return 100.0; } PhobosTrajectory* PhobosTrajectory::LoadFromStream(PhobosStreamReader& Stm) diff --git a/src/Utilities/Enum.h b/src/Utilities/Enum.h index f58794b211..f141815507 100644 --- a/src/Utilities/Enum.h +++ b/src/Utilities/Enum.h @@ -278,6 +278,16 @@ enum class DisplayInfoType : BYTE GattlingStage = 9 }; +enum class ParabolaFireMode +{ + Speed = 0, + Height = 1, + Angle = 2, + SpeedAndHeight = 3, + HeightAndAngle = 4, + SpeedAndAngle = 5, +}; + class MouseCursorHotSpotX { public: diff --git a/src/Utilities/TemplateDef.h b/src/Utilities/TemplateDef.h index b42486f542..b0c3cf547b 100644 --- a/src/Utilities/TemplateDef.h +++ b/src/Utilities/TemplateDef.h @@ -1369,6 +1369,44 @@ if(_strcmpi(parser.value(), #name) == 0){ value = __uuidof(name ## LocomotionCla return false; } + template <> + inline bool read(ParabolaFireMode &value, INI_EX &parser, const char *pSection, const char *pKey) + { + if (parser.ReadString(pSection, pKey)) + { + if (_strcmpi(parser.value(), "height") == 0) + { + value = ParabolaFireMode::Height; + } + else if (_strcmpi(parser.value(), "angle") == 0) + { + value = ParabolaFireMode::Angle; + } + else if (_strcmpi(parser.value(), "speedandheight") == 0) + { + value = ParabolaFireMode::SpeedAndHeight; + } + else if (_strcmpi(parser.value(), "heightandangle") == 0) + { + value = ParabolaFireMode::HeightAndAngle; + } + else if (_strcmpi(parser.value(), "speedandangle") == 0) + { + value = ParabolaFireMode::SpeedAndAngle; + } + else + { + if (_strcmpi(parser.value(), "speed") != 0) + Debug::INIParseFailed(pSection, pKey, parser.value(), "Parabola fire mode is invalid, default to Speed"); + value = ParabolaFireMode::Speed; + } + + return true; + } + + return false; + } + template void parse_values(std::vector& vector, INI_EX& parser, const char* pSection, const char* pKey) { From 4742533fe4afbc61fd8643a521d8a75051011eaf Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Thu, 5 Sep 2024 16:58:35 +0800 Subject: [PATCH 06/32] Update doc --- docs/New-or-Enhanced-Logics.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index f5c780eebc..55b902f4ff 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -708,12 +708,12 @@ Trajectory.Bombard.Height=0.0 ; double - `Trajectory.Parabola.DetonationDistance` controls the maximum distance in cells from intended target (checked at start of each game frame, before the projectile moves) at which the projectile will be forced to detonate. Set to 0 to disable forced detonation. - `Trajectory.Parabola.TargetSnapDistance` controls the maximum distance in cells from intended target the projectile can be at moment of detonation to make the projectile 'snap' on the intended target. Set to 0 to disable snapping. - `Trajectory.Parabola.OpenFireMode` controls how should the projectile be launched. This has the following 6 modes. - - 0 - Automatic calculation mode with fixed horizontal velocity, using `Trajectory.Speed` and target coordinates as calculation conditions, i.e. the flight time of the projectile is permanently fixed. - - 1 - Automatic calculation mode with fixed maximum height, useing `Trajectory.Parabola.ThrowHeight` and target coordinates as calculation conditions, i.e. the detonation time of the projectile is relatively fixed. - - 2 - Automatic calculation mode with fixed fire angle, useing `Trajectory.Parabola.LaunchAngle` and target coordinates as calculation conditions. In this mode, the performance consumption is high, and may have no solution. It is not recommended to enable `SubjectToCliffs` or enable `AA` with a smaller `MinimumRange` when using this mode. - - 3 - Fixed horizontal velocity and maximum height mode, using `Trajectory.Speed` and `Trajectory.Parabola.ThrowHeight` as calculation conditions, i.e. the trajectory will only undergo altitude changes with the height of the target. - - 4 - Fixed maximum height and fire angle mode, using `Trajectory.Parabola.ThrowHeight` and `Trajectory.Parabola.LaunchAngle` as calculation conditions, i.e. the trajectory will change horizontally with the height of the target. - - 5 - Fixed horizontal velocity and fire angle mode, using `Trajectory.Speed` and `Trajectory.Parabola.LaunchAngle` as calculation conditions, i.e. the trajectory will be permanently fixed. + - Speed - Automatic calculation mode with fixed horizontal velocity, using `Trajectory.Speed` and target coordinates as calculation conditions, i.e. the flight time of the projectile is permanently fixed. + - Height - Automatic calculation mode with fixed maximum height, useing `Trajectory.Parabola.ThrowHeight` and target coordinates as calculation conditions, i.e. the detonation time of the projectile is relatively fixed. + - Angle - Automatic calculation mode with fixed fire angle, useing `Trajectory.Parabola.LaunchAngle` and target coordinates as calculation conditions. In this mode, the performance consumption is high, and may have no solution. It is not recommended to enable `SubjectToCliffs` or enable `AA` with a smaller `MinimumRange` when using this mode. + - SpeedAndHeight - Fixed horizontal velocity and maximum height mode, using `Trajectory.Speed` and `Trajectory.Parabola.ThrowHeight` as calculation conditions, i.e. the trajectory will only undergo altitude changes with the height of the target. + - HeightAndAngle - Fixed maximum height and fire angle mode, using `Trajectory.Parabola.ThrowHeight` and `Trajectory.Parabola.LaunchAngle` as calculation conditions, i.e. the trajectory will change horizontally with the height of the target. + - SpeedAndAngle - Fixed horizontal velocity and fire angle mode, using `Trajectory.Speed` and `Trajectory.Parabola.LaunchAngle` as calculation conditions, i.e. the trajectory will be permanently fixed. - `Trajectory.Parabola.ThrowHeight` controls the maximum height of the projectile and is only used for modes 1, 3, or 4. The specific height will be determined by taking the larger of the launch height and the target height then increasing this value. Non positive numbers are not supported. - `Trajectory.Parabola.LaunchAngle` controls the fire angle of the projectile and is only used for modes 2, 4, or 5. Only supports -90.0 ~ 90.0 (Cannot use boundary values) in Mode 2 or 5, and 0.0 ~ 90.0 (Cannot use boundary values) in Mode 4. - `Trajectory.Parabola.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. @@ -737,7 +737,7 @@ In `rulesmd.ini`: Trajectory=Parabola ; Trajectory type Trajectory.Parabola.DetonationDistance=0.4 ; floating point value Trajectory.Parabola.TargetSnapDistance=0.5 ; floating point value -Trajectory.Parabola.OpenFireMode=0 ; integer - Six different modes +Trajectory.Parabola.OpenFireMode=0 ; ParabolaFireMode value enumeration (speed|height|angle|speedandheight|heightandangle|speedandangle) Trajectory.Parabola.ThrowHeight=600 ; integer Trajectory.Parabola.LaunchAngle=30 ; floating point value Trajectory.Parabola.LeadTimeCalculate=no ; boolean From 22579b770cc249c4098fe8e290f1195162e9101f Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Mon, 9 Sep 2024 19:43:55 +0800 Subject: [PATCH 07/32] Small fix --- src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp index 72fd9bf962..33ac59ec42 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp @@ -256,8 +256,10 @@ void ParabolaTrajectory::OnAIPreDetonate(BulletClass* pBullet) pExt->SnappedToTarget = true; pBullet->SetLocation(coords); } - else if (const int cellHeight = MapClass::Instance->GetCellFloorHeight(pBullet->Location)) + else { + const int cellHeight = MapClass::Instance->GetCellFloorHeight(pBullet->Location); + if (pBullet->Location.Z < cellHeight) pBullet->SetLocation(CoordStruct{ pBullet->Location.X, pBullet->Location.Y, cellHeight }); } From 6b22a627e073d7f82ff02fd7e5acb707848241d0 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Wed, 11 Sep 2024 01:58:05 +0800 Subject: [PATCH 08/32] fix doc --- docs/New-or-Enhanced-Logics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 55b902f4ff..bcaa849f3c 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -737,7 +737,7 @@ In `rulesmd.ini`: Trajectory=Parabola ; Trajectory type Trajectory.Parabola.DetonationDistance=0.4 ; floating point value Trajectory.Parabola.TargetSnapDistance=0.5 ; floating point value -Trajectory.Parabola.OpenFireMode=0 ; ParabolaFireMode value enumeration (speed|height|angle|speedandheight|heightandangle|speedandangle) +Trajectory.Parabola.OpenFireMode=speed ; ParabolaFireMode value enumeration (speed|height|angle|speedandheight|heightandangle|speedandangle) Trajectory.Parabola.ThrowHeight=600 ; integer Trajectory.Parabola.LaunchAngle=30 ; floating point value Trajectory.Parabola.LeadTimeCalculate=no ; boolean From ce32f2cc56d37a2a256a0f66913c3b668429d5e8 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Fri, 20 Sep 2024 01:56:46 +0800 Subject: [PATCH 09/32] Change read enum --- docs/New-or-Enhanced-Logics.md | 2 +- .../Trajectories/ParabolaTrajectory.cpp | 18 ++++++++- .../Bullet/Trajectories/ParabolaTrajectory.h | 10 +++++ src/Utilities/Enum.h | 10 ----- src/Utilities/TemplateDef.h | 38 ------------------- 5 files changed, 28 insertions(+), 50 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 7d1b7667c7..8588b28728 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -740,7 +740,7 @@ In `rulesmd.ini`: Trajectory=Parabola ; Trajectory type Trajectory.Parabola.DetonationDistance=0.4 ; floating point value Trajectory.Parabola.TargetSnapDistance=0.5 ; floating point value -Trajectory.Parabola.OpenFireMode=speed ; ParabolaFireMode value enumeration (speed|height|angle|speedandheight|heightandangle|speedandangle) +Trajectory.Parabola.OpenFireMode=Speed ; ParabolaFireMode value enumeration (Speed|Height|Angle|SpeedAndHeight|HeightAndAngle|SpeedAndAngle) Trajectory.Parabola.ThrowHeight=600 ; integer Trajectory.Parabola.LaunchAngle=30 ; floating point value Trajectory.Parabola.LeadTimeCalculate=no ; boolean diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp index 33ac59ec42..d5d2a730f5 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp @@ -75,7 +75,23 @@ void ParabolaTrajectoryType::Read(CCINIClass* const pINI, const char* pSection) INI_EX exINI(pINI); this->DetonationDistance.Read(exINI, pSection, "Trajectory.Parabola.DetonationDistance"); this->TargetSnapDistance.Read(exINI, pSection, "Trajectory.Parabola.TargetSnapDistance"); - this->OpenFireMode.Read(exINI, pSection, "Trajectory.Parabola.OpenFireMode"); + + pINI->ReadString(pSection, "Trajectory.Parabola.OpenFireMode", "", Phobos::readBuffer); + if (INIClass::IsBlank(Phobos::readBuffer)) + this->OpenFireMode = ParabolaFireMode::Speed; + else if (_stricmp(Phobos::readBuffer, "height") == 0) + this->OpenFireMode = ParabolaFireMode::Height; + else if (_stricmp(Phobos::readBuffer, "angle") == 0) + this->OpenFireMode = ParabolaFireMode::Angle; + else if (_stricmp(Phobos::readBuffer, "speedandheight") == 0) + this->OpenFireMode = ParabolaFireMode::SpeedAndHeight; + else if (_stricmp(Phobos::readBuffer, "heightandangle") == 0) + this->OpenFireMode = ParabolaFireMode::HeightAndAngle; + else if (_stricmp(Phobos::readBuffer, "speedandangle") == 0) + this->OpenFireMode = ParabolaFireMode::SpeedAndAngle; + else + this->OpenFireMode = ParabolaFireMode::Speed; + this->ThrowHeight.Read(exINI, pSection, "Trajectory.Parabola.ThrowHeight"); this->LaunchAngle.Read(exINI, pSection, "Trajectory.Parabola.LaunchAngle"); this->LeadTimeCalculate.Read(exINI, pSection, "Trajectory.Parabola.LeadTimeCalculate"); diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h index c49325f3ae..3670e2419f 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h @@ -2,6 +2,16 @@ #include "PhobosTrajectory.h" +enum class ParabolaFireMode +{ + Speed = 0, + Height = 1, + Angle = 2, + SpeedAndHeight = 3, + HeightAndAngle = 4, + SpeedAndAngle = 5, +}; + class ParabolaTrajectoryType final : public PhobosTrajectoryType { public: diff --git a/src/Utilities/Enum.h b/src/Utilities/Enum.h index f141815507..f58794b211 100644 --- a/src/Utilities/Enum.h +++ b/src/Utilities/Enum.h @@ -278,16 +278,6 @@ enum class DisplayInfoType : BYTE GattlingStage = 9 }; -enum class ParabolaFireMode -{ - Speed = 0, - Height = 1, - Angle = 2, - SpeedAndHeight = 3, - HeightAndAngle = 4, - SpeedAndAngle = 5, -}; - class MouseCursorHotSpotX { public: diff --git a/src/Utilities/TemplateDef.h b/src/Utilities/TemplateDef.h index b0c3cf547b..b42486f542 100644 --- a/src/Utilities/TemplateDef.h +++ b/src/Utilities/TemplateDef.h @@ -1369,44 +1369,6 @@ if(_strcmpi(parser.value(), #name) == 0){ value = __uuidof(name ## LocomotionCla return false; } - template <> - inline bool read(ParabolaFireMode &value, INI_EX &parser, const char *pSection, const char *pKey) - { - if (parser.ReadString(pSection, pKey)) - { - if (_strcmpi(parser.value(), "height") == 0) - { - value = ParabolaFireMode::Height; - } - else if (_strcmpi(parser.value(), "angle") == 0) - { - value = ParabolaFireMode::Angle; - } - else if (_strcmpi(parser.value(), "speedandheight") == 0) - { - value = ParabolaFireMode::SpeedAndHeight; - } - else if (_strcmpi(parser.value(), "heightandangle") == 0) - { - value = ParabolaFireMode::HeightAndAngle; - } - else if (_strcmpi(parser.value(), "speedandangle") == 0) - { - value = ParabolaFireMode::SpeedAndAngle; - } - else - { - if (_strcmpi(parser.value(), "speed") != 0) - Debug::INIParseFailed(pSection, pKey, parser.value(), "Parabola fire mode is invalid, default to Speed"); - value = ParabolaFireMode::Speed; - } - - return true; - } - - return false; - } - template void parse_values(std::vector& vector, INI_EX& parser, const char* pSection, const char* pKey) { From 81751dfbbce31c49ea15ee22488762536f3bd140 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Sat, 21 Sep 2024 01:31:45 +0800 Subject: [PATCH 10/32] New function cooperate with Disperse --- docs/New-or-Enhanced-Logics.md | 2 +- .../Bullet/Trajectories/ParabolaTrajectory.cpp | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 8588b28728..9c2cd0c438 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -708,7 +708,7 @@ Trajectory.Bombard.Height=0.0 ; double #### Parabola trajectory - As the name says, this is a completely reset `Arcing` with different enhanced functions. Without doubt, It supported linkage with `Trajectory=Disperse`. - - `Trajectory.Parabola.DetonationDistance` controls the maximum distance in cells from intended target (checked at start of each game frame, before the projectile moves) at which the projectile will be forced to detonate. Set to 0 to disable forced detonation. + - `Trajectory.Parabola.DetonationDistance` controls the maximum distance in cells from intended target (checked at start of each game frame, before the projectile moves) at which the projectile will be forced to detonate. Set to 0 to disable forced detonation. More specifically, when it is set to a negative value, if the target is movable, it will change its target to the cell where the target is located (This is a function expanded for `Disperse` and `Airburst` purposes). - `Trajectory.Parabola.TargetSnapDistance` controls the maximum distance in cells from intended target the projectile can be at moment of detonation to make the projectile 'snap' on the intended target. Set to 0 to disable snapping. - `Trajectory.Parabola.OpenFireMode` controls how should the projectile be launched. This has the following 6 modes. - Speed - Automatic calculation mode with fixed horizontal velocity, using `Trajectory.Speed` and target coordinates as calculation conditions, i.e. the flight time of the projectile is permanently fixed. diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp index d5d2a730f5..2ac77beb3c 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp @@ -220,6 +220,19 @@ void ParabolaTrajectory::OnUnlimbo(BulletClass* pBullet, CoordStruct* pCoord, Bu this->LastVelocity = BulletVelocity::Empty; pBullet->Velocity = BulletVelocity::Empty; + FootClass* const pTarget = abstract_cast(pBullet->Target); + bool resetTarget = false; + + if (this->DetonationDistance <= -1e-10 && pTarget) + { + if (CellClass* const pCell = MapClass::Instance->TryGetCellAt(pTarget->GetCoords())) + { + pBullet->Target = pCell; + pBullet->TargetCoords = pCell->GetCoords(); + resetTarget = true; + } + } + if (WeaponTypeClass* const pWeapon = pBullet->WeaponType) this->CountOfBurst = pWeapon->Burst; @@ -231,7 +244,7 @@ void ParabolaTrajectory::OnUnlimbo(BulletClass* pBullet, CoordStruct* pCoord, Bu this->OffsetCoord.Y = -(this->OffsetCoord.Y); } - if (!this->LeadTimeCalculate || !abstract_cast(pBullet->Target)) + if (!this->LeadTimeCalculate || !pTarget || resetTarget) this->PrepareForOpenFire(pBullet); else this->WaitOneFrame.Start(1); From 228d88bd896eefb721a18d44d8ddabb0a2f35ffe Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Tue, 24 Sep 2024 01:46:43 +0800 Subject: [PATCH 11/32] Update with new template --- .../Trajectories/ParabolaTrajectory.cpp | 116 ++++-------------- .../Bullet/Trajectories/ParabolaTrajectory.h | 64 +++++----- .../Bullet/Trajectories/PhobosTrajectory.cpp | 2 +- 3 files changed, 54 insertions(+), 128 deletions(-) diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp index 2ac77beb3c..a574070567 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp @@ -5,40 +5,14 @@ #include #include -bool ParabolaTrajectoryType::Load(PhobosStreamReader& Stm, bool RegisterForChange) +PhobosTrajectory* ParabolaTrajectoryType::CreateInstance() const { - this->PhobosTrajectoryType::Load(Stm, false); - - Stm - .Process(this->DetonationDistance, false) - .Process(this->TargetSnapDistance, false) - .Process(this->OpenFireMode, false) - .Process(this->ThrowHeight, false) - .Process(this->LaunchAngle, false) - .Process(this->LeadTimeCalculate, false) - .Process(this->LeadTimeSimplify, false) - .Process(this->LeadTimeMultiplier, false) - .Process(this->DetonationAngle, false) - .Process(this->DetonationHeight, false) - .Process(this->BounceTimes, false) - .Process(this->BounceOnWater, false) - .Process(this->BounceDetonate, false) - .Process(this->BounceAttenuation, false) - .Process(this->BounceCoefficient, false) - .Process(this->OffsetCoord, false) - .Process(this->RotateCoord, false) - .Process(this->MirrorCoord, false) - .Process(this->UseDisperseBurst, false) - .Process(this->AxisOfRotation, false) - ; - - return true; + return new ParabolaTrajectory(this); } -bool ParabolaTrajectoryType::Save(PhobosStreamWriter& Stm) const +template +void ParabolaTrajectoryType::Serialize(T& Stm) { - this->PhobosTrajectoryType::Save(Stm); - Stm .Process(this->DetonationDistance) .Process(this->TargetSnapDistance) @@ -61,13 +35,20 @@ bool ParabolaTrajectoryType::Save(PhobosStreamWriter& Stm) const .Process(this->UseDisperseBurst) .Process(this->AxisOfRotation) ; +} +bool ParabolaTrajectoryType::Load(PhobosStreamReader& Stm, bool RegisterForChange) +{ + this->PhobosTrajectoryType::Load(Stm, false); + this->Serialize(Stm); return true; } -PhobosTrajectory* ParabolaTrajectoryType::CreateInstance() const +bool ParabolaTrajectoryType::Save(PhobosStreamWriter& Stm) const { - return new ParabolaTrajectory(this); + this->PhobosTrajectoryType::Save(Stm); + const_cast(this)->Serialize(Stm); + return true; } void ParabolaTrajectoryType::Read(CCINIClass* const pINI, const char* pSection) @@ -111,10 +92,9 @@ void ParabolaTrajectoryType::Read(CCINIClass* const pINI, const char* pSection) this->AxisOfRotation.Read(exINI, pSection, "Trajectory.Parabola.AxisOfRotation"); } -bool ParabolaTrajectory::Load(PhobosStreamReader& Stm, bool RegisterForChange) +template +void ParabolaTrajectory::Serialize(T& Stm) { - this->PhobosTrajectory::Load(Stm, false); - Stm .Process(this->DetonationDistance) .Process(this->TargetSnapDistance) @@ -145,79 +125,25 @@ bool ParabolaTrajectory::Load(PhobosStreamReader& Stm, bool RegisterForChange) .Process(this->WaitOneFrame) .Process(this->LastVelocity) ; +} +bool ParabolaTrajectory::Load(PhobosStreamReader& Stm, bool RegisterForChange) +{ + this->PhobosTrajectory::Load(Stm, false); + this->Serialize(Stm); return true; } bool ParabolaTrajectory::Save(PhobosStreamWriter& Stm) const { this->PhobosTrajectory::Save(Stm); - - Stm - .Process(this->DetonationDistance) - .Process(this->TargetSnapDistance) - .Process(this->OpenFireMode) - .Process(this->ThrowHeight) - .Process(this->LaunchAngle) - .Process(this->LeadTimeCalculate) - .Process(this->LeadTimeSimplify) - .Process(this->LeadTimeMultiplier) - .Process(this->DetonationAngle) - .Process(this->DetonationHeight) - .Process(this->BounceTimes) - .Process(this->BounceOnWater) - .Process(this->BounceDetonate) - .Process(this->BounceAttenuation) - .Process(this->BounceCoefficient) - .Process(this->OffsetCoord) - .Process(this->RotateCoord) - .Process(this->MirrorCoord) - .Process(this->UseDisperseBurst) - .Process(this->AxisOfRotation) - .Process(this->ShouldDetonate) - .Process(this->ShouldBounce) - .Process(this->NeedExtraCheck) - .Process(this->LastTargetCoord) - .Process(this->CurrentBurst) - .Process(this->CountOfBurst) - .Process(this->WaitOneFrame) - .Process(this->LastVelocity) - ; - + const_cast(this)->Serialize(Stm); return true; } void ParabolaTrajectory::OnUnlimbo(BulletClass* pBullet, CoordStruct* pCoord, BulletVelocity* pVelocity) { - auto const pType = this->GetTrajectoryType(pBullet); - - this->DetonationDistance = pType->DetonationDistance; - this->TargetSnapDistance = pType->TargetSnapDistance; - this->OpenFireMode = pType->OpenFireMode; - this->ThrowHeight = pType->ThrowHeight > 0 ? pType->ThrowHeight : 600; - this->LaunchAngle = pType->LaunchAngle; - this->LeadTimeCalculate = pType->LeadTimeCalculate; - this->LeadTimeSimplify = pType->LeadTimeSimplify; - this->LeadTimeMultiplier = pType->LeadTimeMultiplier; - this->DetonationAngle = pType->DetonationAngle; - this->DetonationHeight = pType->DetonationHeight; - this->BounceTimes = pType->BounceTimes; - this->BounceOnWater = pType->BounceOnWater; - this->BounceDetonate = pType->BounceDetonate; - this->BounceAttenuation = pType->BounceAttenuation; - this->BounceCoefficient = pType->BounceCoefficient; - this->OffsetCoord = pType->OffsetCoord; - this->RotateCoord = pType->RotateCoord; - this->MirrorCoord = pType->MirrorCoord; - this->UseDisperseBurst = pType->UseDisperseBurst; - this->AxisOfRotation = pType->AxisOfRotation; - this->ShouldDetonate = false; - this->ShouldBounce = false; - this->NeedExtraCheck = false; this->LastTargetCoord = pBullet->TargetCoords; - this->CurrentBurst = 0; - this->CountOfBurst = 0; - this->LastVelocity = BulletVelocity::Empty; pBullet->Velocity = BulletVelocity::Empty; FootClass* const pTarget = abstract_cast(pBullet->Target); diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h index 3670e2419f..5b5ccf782b 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h @@ -63,42 +63,16 @@ class ParabolaTrajectoryType final : public PhobosTrajectoryType Valueable MirrorCoord; Valueable UseDisperseBurst; Valueable AxisOfRotation; - // The faster the projectile's speed, the worse the visual effect of bounce function. + +private: + template + void Serialize(T& Stm); }; class ParabolaTrajectory final : public PhobosTrajectory { public: - ParabolaTrajectory() : PhobosTrajectory(TrajectoryFlag::Parabola) - , DetonationDistance { Leptons(102) } - , TargetSnapDistance { Leptons(128) } - , OpenFireMode { ParabolaFireMode::Speed } - , ThrowHeight { 600 } - , LaunchAngle { 30.0 } - , LeadTimeCalculate { false } - , LeadTimeSimplify { false } - , LeadTimeMultiplier { 1.0 } - , DetonationAngle { -90.0 } - , DetonationHeight { -1 } - , BounceTimes { 0 } - , BounceOnWater { false } - , BounceDetonate { false } - , BounceAttenuation { 0.8 } - , BounceCoefficient { 0.8 } - , OffsetCoord {} - , RotateCoord { 0 } - , MirrorCoord { true } - , UseDisperseBurst { false } - , AxisOfRotation {} - , ShouldDetonate { false } - , ShouldBounce { false } - , NeedExtraCheck { false } - , LastTargetCoord {} - , CurrentBurst { 0 } - , CountOfBurst { 0 } - , WaitOneFrame {} - , LastVelocity {} - {} + ParabolaTrajectory(noinit_t) :PhobosTrajectory { noinit_t{} } { } ParabolaTrajectory(PhobosTrajectoryType const* pType) : PhobosTrajectory(TrajectoryFlag::Parabola) , DetonationDistance { Leptons(102) } @@ -129,7 +103,30 @@ class ParabolaTrajectory final : public PhobosTrajectory , CountOfBurst { 0 } , WaitOneFrame {} , LastVelocity {} - {} + { + auto const pFinalType = static_cast(pType); + + this->DetonationDistance = pFinalType->DetonationDistance; + this->TargetSnapDistance = pFinalType->TargetSnapDistance; + this->OpenFireMode = pFinalType->OpenFireMode; + this->ThrowHeight = pFinalType->ThrowHeight > 0 ? pFinalType->ThrowHeight : 600; + this->LaunchAngle = pFinalType->LaunchAngle; + this->LeadTimeCalculate = pFinalType->LeadTimeCalculate; + this->LeadTimeSimplify = pFinalType->LeadTimeSimplify; + this->LeadTimeMultiplier = pFinalType->LeadTimeMultiplier; + this->DetonationAngle = pFinalType->DetonationAngle; + this->DetonationHeight = pFinalType->DetonationHeight; + this->BounceTimes = pFinalType->BounceTimes; + this->BounceOnWater = pFinalType->BounceOnWater; + this->BounceDetonate = pFinalType->BounceDetonate; + this->BounceAttenuation = pFinalType->BounceAttenuation; + this->BounceCoefficient = pFinalType->BounceCoefficient; + this->OffsetCoord = pFinalType->OffsetCoord; + this->RotateCoord = pFinalType->RotateCoord; + this->MirrorCoord = pFinalType->MirrorCoord; + this->UseDisperseBurst = pFinalType->UseDisperseBurst; + this->AxisOfRotation = pFinalType->AxisOfRotation; + } virtual bool Load(PhobosStreamReader& Stm, bool RegisterForChange) override; virtual bool Save(PhobosStreamWriter& Stm) const override; @@ -171,6 +168,9 @@ class ParabolaTrajectory final : public PhobosTrajectory BulletVelocity LastVelocity; private: + template + void Serialize(T& Stm); + void PrepareForOpenFire(BulletClass* pBullet); bool BulletPrepareCheck(BulletClass* pBullet); void CalculateBulletVelocityRightNow(BulletClass* pBullet, CoordStruct* pSourceCoords, double gravity); diff --git a/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp b/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp index 78e976850f..5913b1e7af 100644 --- a/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp @@ -147,7 +147,7 @@ PhobosTrajectory* PhobosTrajectory::LoadFromStream(PhobosStreamReader& Stm) pTraj = new BombardTrajectory(noinit_t {}); break; case TrajectoryFlag::Parabola: - pTraj = DLLCreate(); + pTraj = new ParabolaTrajectory(noinit_t {}); break; default: return nullptr; From a76a734baff777f0834bcc040598663e0095649e Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Sat, 28 Sep 2024 17:01:31 +0800 Subject: [PATCH 12/32] Optimize --- .../Trajectories/ParabolaTrajectory.cpp | 112 +++++++++--------- .../Bullet/Trajectories/ParabolaTrajectory.h | 67 ++--------- 2 files changed, 64 insertions(+), 115 deletions(-) diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp index a574070567..1bbc22f433 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp @@ -96,26 +96,11 @@ template void ParabolaTrajectory::Serialize(T& Stm) { Stm - .Process(this->DetonationDistance) - .Process(this->TargetSnapDistance) - .Process(this->OpenFireMode) + .Process(this->Type) .Process(this->ThrowHeight) - .Process(this->LaunchAngle) - .Process(this->LeadTimeCalculate) - .Process(this->LeadTimeSimplify) - .Process(this->LeadTimeMultiplier) - .Process(this->DetonationAngle) - .Process(this->DetonationHeight) .Process(this->BounceTimes) - .Process(this->BounceOnWater) - .Process(this->BounceDetonate) - .Process(this->BounceAttenuation) - .Process(this->BounceCoefficient) .Process(this->OffsetCoord) - .Process(this->RotateCoord) - .Process(this->MirrorCoord) .Process(this->UseDisperseBurst) - .Process(this->AxisOfRotation) .Process(this->ShouldDetonate) .Process(this->ShouldBounce) .Process(this->NeedExtraCheck) @@ -143,13 +128,16 @@ bool ParabolaTrajectory::Save(PhobosStreamWriter& Stm) const void ParabolaTrajectory::OnUnlimbo(BulletClass* pBullet, CoordStruct* pCoord, BulletVelocity* pVelocity) { + if (!this->Type) // After load + this->Type = this->GetTrajectoryType(pBullet); + + ParabolaTrajectoryType* const pType = this->Type; this->LastTargetCoord = pBullet->TargetCoords; pBullet->Velocity = BulletVelocity::Empty; - FootClass* const pTarget = abstract_cast(pBullet->Target); bool resetTarget = false; - if (this->DetonationDistance <= -1e-10 && pTarget) + if (static_cast(pType->DetonationDistance) <= -1e-10 && pTarget) { if (CellClass* const pCell = MapClass::Instance->TryGetCellAt(pTarget->GetCoords())) { @@ -166,11 +154,11 @@ void ParabolaTrajectory::OnUnlimbo(BulletClass* pBullet, CoordStruct* pCoord, Bu { this->CurrentBurst = pOwner->CurrentBurstIndex; - if (this->MirrorCoord && pOwner->CurrentBurstIndex % 2 == 1) + if (pType->MirrorCoord && pOwner->CurrentBurstIndex % 2 == 1) this->OffsetCoord.Y = -(this->OffsetCoord.Y); } - if (!this->LeadTimeCalculate || !pTarget || resetTarget) + if (!pType->LeadTimeCalculate || !pTarget || resetTarget) this->PrepareForOpenFire(pBullet); else this->WaitOneFrame.Start(1); @@ -178,6 +166,9 @@ void ParabolaTrajectory::OnUnlimbo(BulletClass* pBullet, CoordStruct* pCoord, Bu bool ParabolaTrajectory::OnAI(BulletClass* pBullet) { + if (!this->Type) // After load + this->Type = this->GetTrajectoryType(pBullet); + if (this->WaitOneFrame.IsTicking() && this->BulletPrepareCheck(pBullet)) return false; @@ -192,20 +183,22 @@ bool ParabolaTrajectory::OnAI(BulletClass* pBullet) const double gravity = BulletTypeExt::GetAdjustedGravity(pBullet->Type); if (this->ShouldBounce && this->BounceTimes > 0) - return (pCell->LandType == LandType::Water && !this->BounceOnWater) || this->CalculateBulletVelocityAfterBounce(pBullet, pCell, gravity); + return (pCell->LandType == LandType::Water && !this->Type->BounceOnWater) || this->CalculateBulletVelocityAfterBounce(pBullet, pCell, gravity); return this->BulletDetonateLastCheck(pBullet, gravity); } void ParabolaTrajectory::OnAIPreDetonate(BulletClass* pBullet) { - if (this->TargetSnapDistance <= 0) + const Leptons targetSnapDistance = this->Type->TargetSnapDistance; + + if (targetSnapDistance <= 0) return; const ObjectClass* const pTarget = abstract_cast(pBullet->Target); const CoordStruct coords = pTarget ? pTarget->GetCoords() : pBullet->Data.Location; - if (coords.DistanceFrom(pBullet->Location) <= this->TargetSnapDistance) + if (coords.DistanceFrom(pBullet->Location) <= targetSnapDistance) { auto const pExt = BulletExt::ExtMap.Find(pBullet); pExt->SnappedToTarget = true; @@ -237,14 +230,15 @@ TrajectoryCheckReturnType ParabolaTrajectory::OnAITechnoCheck(BulletClass* pBull void ParabolaTrajectory::PrepareForOpenFire(BulletClass* pBullet) { + ParabolaTrajectoryType* const pType = this->Type; const AbstractClass* const pTarget = pBullet->Target; - bool leadTimeCalculate = this->LeadTimeCalculate && pTarget; + bool leadTimeCalculate = pType->LeadTimeCalculate && pTarget; CoordStruct theTargetCoords = leadTimeCalculate ? pTarget->GetCoords() : pBullet->TargetCoords; CoordStruct theSourceCoords = leadTimeCalculate ? pBullet->Location : pBullet->SourceCoords; leadTimeCalculate &= theTargetCoords != this->LastTargetCoord; double rotateAngle = 0.0; - if (!this->LeadTimeCalculate && theTargetCoords == theSourceCoords && pBullet->Owner) //For disperse. + if (!pType->LeadTimeCalculate && theTargetCoords == theSourceCoords && pBullet->Owner) //For disperse. { const CoordStruct theOwnerCoords = pBullet->Owner->GetCoords(); rotateAngle = Math::atan2(theTargetCoords.Y - theOwnerCoords.Y , theTargetCoords.X - theOwnerCoords.X); @@ -286,13 +280,15 @@ void ParabolaTrajectory::PrepareForOpenFire(BulletClass* pBullet) else this->CalculateBulletVelocityRightNow(pBullet, &theSourceCoords, gravity); - if (!this->UseDisperseBurst && abs(this->RotateCoord) > 1e-10 && this->CountOfBurst > 1) + if (!this->UseDisperseBurst && abs(pType->RotateCoord) > 1e-10 && this->CountOfBurst > 1) { + const CoordStruct axis = pType->AxisOfRotation; + BulletVelocity rotationAxis { - this->AxisOfRotation.X * Math::cos(rotateAngle) + this->AxisOfRotation.Y * Math::sin(rotateAngle), - this->AxisOfRotation.X * Math::sin(rotateAngle) - this->AxisOfRotation.Y * Math::cos(rotateAngle), - static_cast(this->AxisOfRotation.Z) + axis.X * Math::cos(rotateAngle) + axis.Y * Math::sin(rotateAngle), + axis.X * Math::sin(rotateAngle) - axis.Y * Math::cos(rotateAngle), + static_cast(axis.Z) }; const double rotationAxisLengthSquared = rotationAxis.MagnitudeSquared(); @@ -302,16 +298,16 @@ void ParabolaTrajectory::PrepareForOpenFire(BulletClass* pBullet) double extraRotate = 0.0; rotationAxis *= 1 / sqrt(rotationAxisLengthSquared); - if (this->MirrorCoord) + if (pType->MirrorCoord) { if (pBullet->Owner && pBullet->Owner->CurrentBurstIndex % 2 == 1) rotationAxis *= -1; - extraRotate = Math::Pi * (this->RotateCoord * ((this->CurrentBurst / 2) / (this->CountOfBurst - 1.0) - 0.5)) / 180; + extraRotate = Math::Pi * (pType->RotateCoord * ((this->CurrentBurst / 2) / (this->CountOfBurst - 1.0) - 0.5)) / 180; } else { - extraRotate = Math::Pi * (this->RotateCoord * (this->CurrentBurst / (this->CountOfBurst - 1.0) - 0.5)) / 180; + extraRotate = Math::Pi * (pType->RotateCoord * (this->CurrentBurst / (this->CountOfBurst - 1.0) - 0.5)) / 180; } const double cosRotate = Math::cos(extraRotate); @@ -333,13 +329,15 @@ bool ParabolaTrajectory::BulletPrepareCheck(BulletClass* pBullet) void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, CoordStruct* pSourceCoords, double gravity) { - if (this->LeadTimeSimplify) // Only simple guess, not exact solution + ParabolaTrajectoryType* const pType = this->Type; + + if (pType->LeadTimeSimplify) // Only simple guess, not exact solution { int leadTime = 0; // Step 1: Guess the time of encounter between the projectile and the target based on known conditions // Directly assume that the distance between the position where the projectile hits the target and the starting point is 4 grids - switch (this->OpenFireMode) + switch (pType->OpenFireMode) { case ParabolaFireMode::Height: case ParabolaFireMode::HeightAndAngle: @@ -350,7 +348,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C } case ParabolaFireMode::Angle: { - double radian = this->LaunchAngle * Math::Pi / 180.0; + double radian = pType->LaunchAngle * Math::Pi / 180.0; radian = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? (Math::HalfPi / 3) : radian; const double factor = Math::cos(radian); @@ -379,7 +377,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C } // Step 2: Substitute the time into the calculation of the attack coordinates - pBullet->TargetCoords += (pBullet->Target->GetCoords() - this->LastTargetCoord) * (this->LeadTimeMultiplier * leadTime); + pBullet->TargetCoords += (pBullet->Target->GetCoords() - this->LastTargetCoord) * (pType->LeadTimeMultiplier * leadTime); // Step 3: Calculate the parabolic starting point vector this->CalculateBulletVelocityRightNow(pBullet, pSourceCoords, gravity); @@ -390,9 +388,9 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C CoordStruct offsetCoords = pBullet->TargetCoords - targetCoords; // A coefficient that should not exist here normally, but even so, there are still errors - const double speedFixMult = this->LeadTimeMultiplier * 0.75; + const double speedFixMult = pType->LeadTimeMultiplier * 0.75; - switch (this->OpenFireMode) + switch (pType->OpenFireMode) { case ParabolaFireMode::Height: // Fixed max height and aim at the target { @@ -425,7 +423,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C case ParabolaFireMode::Angle: // Fixed fire angle and aim at the target { // Step 1: Read the appropriate fire angle - double radian = this->LaunchAngle * Math::Pi / 180.0; + double radian = pType->LaunchAngle * Math::Pi / 180.0; radian = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? (Math::HalfPi / 3) : radian; // Step 2: Using Newton Iteration Method to determine the time of encounter between the projectile and the target @@ -510,7 +508,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C pBullet->Velocity.Z = sqrt(2 * gravity * (maxHeight - sourceHeight)) + gravity / 2; // Step 6: Read the appropriate fire angle - double radian = this->LaunchAngle * Math::Pi / 180.0; + double radian = pType->LaunchAngle * Math::Pi / 180.0; radian = (radian >= Math::HalfPi || radian <= 1e-10) ? (Math::HalfPi / 3) : radian; // Step 7: Calculate the ratio of horizontal velocity to horizontal distance @@ -553,7 +551,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C const double horizontalVelocity = horizontalDistance * mult; // Step 8: Read the appropriate fire angle - double radian = this->LaunchAngle * Math::Pi / 180.0; + double radian = pType->LaunchAngle * Math::Pi / 180.0; radian = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? (Math::HalfPi / 3) : radian; // Step 9: Calculate the vertical component of the projectile velocity @@ -603,6 +601,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, CoordStruct* pSourceCoords, double gravity) { + ParabolaTrajectoryType* const pType = this->Type; // Calculate horizontal distance const CoordStruct distanceCoords = pBullet->TargetCoords - *pSourceCoords; const double distance = distanceCoords.Magnitude(); @@ -615,7 +614,7 @@ void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, C return; } - switch (this->OpenFireMode) + switch (pType->OpenFireMode) { case ParabolaFireMode::Height: // Fixed max height and aim at the target { @@ -637,7 +636,7 @@ void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, C case ParabolaFireMode::Angle: // Fixed fire angle and aim at the target { // Step 1: Read the appropriate fire angle - double radian = this->LaunchAngle * Math::Pi / 180.0; + double radian = pType->LaunchAngle * Math::Pi / 180.0; // Step 2: Using Newton Iteration Method to determine the projectile velocity double velocity = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? 100.0 : this->SearchVelocity(horizontalDistance, distanceCoords.Z, radian, gravity); @@ -683,7 +682,7 @@ void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, C pBullet->Velocity.Z = sqrt(2 * gravity * (maxHeight - sourceHeight)); // Step 3: Read the appropriate fire angle - double radian = this->LaunchAngle * Math::Pi / 180.0; + double radian = pType->LaunchAngle * Math::Pi / 180.0; radian = (radian >= Math::HalfPi || radian <= 1e-10) ? (Math::HalfPi / 3) : radian; // Step 4: Calculate the ratio of horizontal velocity to horizontal distance @@ -707,7 +706,7 @@ void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, C pBullet->Velocity.Y = distanceCoords.Y * mult; // Step 4: Read the appropriate fire angle - double radian = this->LaunchAngle * Math::Pi / 180.0; + double radian = pType->LaunchAngle * Math::Pi / 180.0; radian = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? (Math::HalfPi / 3) : radian; // Step 5: Calculate the vertical component of the projectile velocity @@ -739,7 +738,7 @@ void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, C void ParabolaTrajectory::CheckIfNeedExtraCheck(BulletClass* pBullet) { - switch (this->OpenFireMode) + switch (this->Type->OpenFireMode) { case ParabolaFireMode::Height: // Fixed max height and aim at the target case ParabolaFireMode::Angle: // Fixed fire angle and aim at the target @@ -940,11 +939,12 @@ bool ParabolaTrajectory::CalculateBulletVelocityAfterBounce(BulletClass* pBullet --this->BounceTimes; this->ShouldBounce = false; + ParabolaTrajectoryType* const pType = this->Type; const BulletVelocity groundNormalVector = this->GetGroundNormalVector(pBullet, pCell); - pBullet->Velocity = (this->LastVelocity - groundNormalVector * (this->LastVelocity * groundNormalVector) * 2) * this->BounceCoefficient; + pBullet->Velocity = (this->LastVelocity - groundNormalVector * (this->LastVelocity * groundNormalVector) * 2) * pType->BounceCoefficient; pBullet->Velocity.Z -= gravity; - if (this->BounceDetonate) + if (pType->BounceDetonate) { TechnoClass* const pFirer = pBullet->Owner; HouseClass* const pOwner = pFirer ? pFirer->Owner : BulletExt::ExtMap.Find(pBullet)->FirerHouse; @@ -953,7 +953,7 @@ bool ParabolaTrajectory::CalculateBulletVelocityAfterBounce(BulletClass* pBullet if (const int damage = pBullet->Health) { - if (const int newDamage = static_cast(damage * this->BounceAttenuation)) + if (const int newDamage = static_cast(damage * pType->BounceAttenuation)) pBullet->Health = newDamage; else pBullet->Health = damage > 0 ? 1 : -1; @@ -1088,30 +1088,32 @@ bool ParabolaTrajectory::BulletDetonatePreCheck(BulletClass* pBullet) if (this->ShouldDetonate) return true; - if (this->DetonationHeight >= 0 && pBullet->Velocity.Z < 1e-10 && (pBullet->Location.Z - pBullet->SourceCoords.Z) < this->DetonationHeight) + ParabolaTrajectoryType* const pType = this->Type; + + if (pType->DetonationHeight >= 0 && pBullet->Velocity.Z < 1e-10 && (pBullet->Location.Z - pBullet->SourceCoords.Z) < pType->DetonationHeight) return true; - if (abs(this->DetonationAngle) < 1e-10) + if (abs(pType->DetonationAngle) < 1e-10) { if (pBullet->Velocity.Z < 1e-10) return true; } - else if (abs(this->DetonationAngle) < 90.0) + else if (abs(pType->DetonationAngle) < 90.0) { const double horizontalVelocity = Vector2D{ pBullet->Velocity.X, pBullet->Velocity.Y }.Magnitude(); if (horizontalVelocity > 1e-10) { - if ((pBullet->Velocity.Z / horizontalVelocity) < Math::tan(this->DetonationAngle * Math::Pi / 180.0)) + if ((pBullet->Velocity.Z / horizontalVelocity) < Math::tan(pType->DetonationAngle * Math::Pi / 180.0)) return true; } - else if (this->DetonationAngle > 1e-10 || pBullet->Velocity.Z < 1e-10) + else if (pType->DetonationAngle > 1e-10 || pBullet->Velocity.Z < 1e-10) { return true; } } - if (this->DetonationDistance > 0 && pBullet->TargetCoords.DistanceFrom(pBullet->Location) < this->DetonationDistance) + if (pBullet->TargetCoords.DistanceFrom(pBullet->Location) < static_cast(pType->DetonationDistance)) return true; return false; diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h index 5b5ccf782b..2a6040340a 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h @@ -75,26 +75,11 @@ class ParabolaTrajectory final : public PhobosTrajectory ParabolaTrajectory(noinit_t) :PhobosTrajectory { noinit_t{} } { } ParabolaTrajectory(PhobosTrajectoryType const* pType) : PhobosTrajectory(TrajectoryFlag::Parabola) - , DetonationDistance { Leptons(102) } - , TargetSnapDistance { Leptons(128) } - , OpenFireMode { ParabolaFireMode::Speed } - , ThrowHeight { 600 } - , LaunchAngle { 30.0 } - , LeadTimeCalculate { false } - , LeadTimeSimplify { false } - , LeadTimeMultiplier { 1.0 } - , DetonationAngle { -90.0 } - , DetonationHeight { -1 } - , BounceTimes { 0 } - , BounceOnWater { false } - , BounceDetonate { false } - , BounceAttenuation { 0.8 } - , BounceCoefficient { 0.8 } - , OffsetCoord {} - , RotateCoord { 0 } - , MirrorCoord { true } - , UseDisperseBurst { false } - , AxisOfRotation {} + , Type { static_cast(const_cast(pType)) } + , ThrowHeight { Type->ThrowHeight > 0 ? Type->ThrowHeight : 600 } + , BounceTimes { Type->BounceTimes } + , OffsetCoord { static_cast(Type->OffsetCoord) } + , UseDisperseBurst { Type->UseDisperseBurst } , ShouldDetonate { false } , ShouldBounce { false } , NeedExtraCheck { false } @@ -103,30 +88,7 @@ class ParabolaTrajectory final : public PhobosTrajectory , CountOfBurst { 0 } , WaitOneFrame {} , LastVelocity {} - { - auto const pFinalType = static_cast(pType); - - this->DetonationDistance = pFinalType->DetonationDistance; - this->TargetSnapDistance = pFinalType->TargetSnapDistance; - this->OpenFireMode = pFinalType->OpenFireMode; - this->ThrowHeight = pFinalType->ThrowHeight > 0 ? pFinalType->ThrowHeight : 600; - this->LaunchAngle = pFinalType->LaunchAngle; - this->LeadTimeCalculate = pFinalType->LeadTimeCalculate; - this->LeadTimeSimplify = pFinalType->LeadTimeSimplify; - this->LeadTimeMultiplier = pFinalType->LeadTimeMultiplier; - this->DetonationAngle = pFinalType->DetonationAngle; - this->DetonationHeight = pFinalType->DetonationHeight; - this->BounceTimes = pFinalType->BounceTimes; - this->BounceOnWater = pFinalType->BounceOnWater; - this->BounceDetonate = pFinalType->BounceDetonate; - this->BounceAttenuation = pFinalType->BounceAttenuation; - this->BounceCoefficient = pFinalType->BounceCoefficient; - this->OffsetCoord = pFinalType->OffsetCoord; - this->RotateCoord = pFinalType->RotateCoord; - this->MirrorCoord = pFinalType->MirrorCoord; - this->UseDisperseBurst = pFinalType->UseDisperseBurst; - this->AxisOfRotation = pFinalType->AxisOfRotation; - } + {} virtual bool Load(PhobosStreamReader& Stm, bool RegisterForChange) override; virtual bool Save(PhobosStreamWriter& Stm) const override; @@ -138,26 +100,11 @@ class ParabolaTrajectory final : public PhobosTrajectory virtual TrajectoryCheckReturnType OnAITargetCoordCheck(BulletClass* pBullet) override; virtual TrajectoryCheckReturnType OnAITechnoCheck(BulletClass* pBullet, TechnoClass* pTechno) override; - Leptons DetonationDistance; - Leptons TargetSnapDistance; - ParabolaFireMode OpenFireMode; + ParabolaTrajectoryType* Type; int ThrowHeight; - double LaunchAngle; - bool LeadTimeCalculate; - bool LeadTimeSimplify; - double LeadTimeMultiplier; - double DetonationAngle; - int DetonationHeight; int BounceTimes; - bool BounceOnWater; - bool BounceDetonate; - double BounceAttenuation; - double BounceCoefficient; CoordStruct OffsetCoord; - int RotateCoord; - bool MirrorCoord; bool UseDisperseBurst; - CoordStruct AxisOfRotation; bool ShouldDetonate; bool ShouldBounce; bool NeedExtraCheck; From d00577b6d3cb6aac7b677a9ea48371dd35084996 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Fri, 4 Oct 2024 18:37:42 +0800 Subject: [PATCH 13/32] Fix merge --- .../Trajectories/ParabolaTrajectory.cpp | 130 +++++++----------- .../Bullet/Trajectories/ParabolaTrajectory.h | 16 +-- .../Bullet/Trajectories/PhobosTrajectory.cpp | 24 ---- 3 files changed, 61 insertions(+), 109 deletions(-) diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp index 1bbc22f433..135f76503c 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp @@ -5,9 +5,9 @@ #include #include -PhobosTrajectory* ParabolaTrajectoryType::CreateInstance() const +std::unique_ptr ParabolaTrajectoryType::CreateInstance() const { - return new ParabolaTrajectory(this); + return std::make_unique(this); } template @@ -128,10 +128,7 @@ bool ParabolaTrajectory::Save(PhobosStreamWriter& Stm) const void ParabolaTrajectory::OnUnlimbo(BulletClass* pBullet, CoordStruct* pCoord, BulletVelocity* pVelocity) { - if (!this->Type) // After load - this->Type = this->GetTrajectoryType(pBullet); - - ParabolaTrajectoryType* const pType = this->Type; + const ParabolaTrajectoryType* const pType = this->Type; this->LastTargetCoord = pBullet->TargetCoords; pBullet->Velocity = BulletVelocity::Empty; FootClass* const pTarget = abstract_cast(pBullet->Target); @@ -166,9 +163,6 @@ void ParabolaTrajectory::OnUnlimbo(BulletClass* pBullet, CoordStruct* pCoord, Bu bool ParabolaTrajectory::OnAI(BulletClass* pBullet) { - if (!this->Type) // After load - this->Type = this->GetTrajectoryType(pBullet); - if (this->WaitOneFrame.IsTicking() && this->BulletPrepareCheck(pBullet)) return false; @@ -230,7 +224,7 @@ TrajectoryCheckReturnType ParabolaTrajectory::OnAITechnoCheck(BulletClass* pBull void ParabolaTrajectory::PrepareForOpenFire(BulletClass* pBullet) { - ParabolaTrajectoryType* const pType = this->Type; + const ParabolaTrajectoryType* const pType = this->Type; const AbstractClass* const pTarget = pBullet->Target; bool leadTimeCalculate = pType->LeadTimeCalculate && pTarget; CoordStruct theTargetCoords = leadTimeCalculate ? pTarget->GetCoords() : pBullet->TargetCoords; @@ -329,7 +323,7 @@ bool ParabolaTrajectory::BulletPrepareCheck(BulletClass* pBullet) void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, CoordStruct* pSourceCoords, double gravity) { - ParabolaTrajectoryType* const pType = this->Type; + const ParabolaTrajectoryType* const pType = this->Type; if (pType->LeadTimeSimplify) // Only simple guess, not exact solution { @@ -371,7 +365,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C default: { // Assuming equal height - leadTime = static_cast((Unsorted::LeptonsPerCell << 2) / this->GetTrajectorySpeed(pBullet)); + leadTime = static_cast((Unsorted::LeptonsPerCell << 2) / this->Speed); break; } } @@ -454,36 +448,33 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C } case ParabolaFireMode::SpeedAndHeight: // Fixed horizontal speed and fixed max height { - // Step 1: Read the appropriate horizontal speed - const double horizontalSpeed = this->GetTrajectorySpeed(pBullet); - - // Step 2: Calculate the time when the projectile meets the target directly using horizontal velocity - const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, horizontalSpeed); + // Step 1: Calculate the time when the projectile meets the target directly using horizontal velocity + const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, this->Speed); - // Step 3: Substitute the time into the calculation of the attack coordinates + // Step 2: Substitute the time into the calculation of the attack coordinates pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; - // Step 4: Check if it is an unsolvable solution + // Step 3: Check if it is an unsolvable solution if (meetTime <= 1e-10 || destinationCoords.Magnitude() <= 1e-10) break; - // Step 5: Calculate the ratio of horizontal velocity to horizontal distance + // Step 4: Calculate the ratio of horizontal velocity to horizontal distance const double horizontalDistance = Point2D{ destinationCoords.X, destinationCoords.Y }.Magnitude(); - const double mult = horizontalDistance > 1e-10 ? horizontalSpeed / horizontalDistance : 1.0; + const double mult = horizontalDistance > 1e-10 ? this->Speed / horizontalDistance : 1.0; - // Step 6: Calculate the horizontal component of the projectile velocity + // Step 5: Calculate the horizontal component of the projectile velocity pBullet->Velocity.X = destinationCoords.X * mult; pBullet->Velocity.Y = destinationCoords.Y * mult; - // Step 7: Determine the maximum height that the projectile should reach + // Step 6: Determine the maximum height that the projectile should reach const int sourceHeight = pSourceCoords->Z, targetHeight = sourceHeight + destinationCoords.Z; const int maxHeight = destinationCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; - // Step 8: Calculate the vertical component of the projectile velocity + // Step 7: Calculate the vertical component of the projectile velocity pBullet->Velocity.Z = sqrt(2 * gravity * (maxHeight - sourceHeight)) + gravity / 2; - // Step 9: Record whether it requires additional checks during the flight + // Step 8: Record whether it requires additional checks during the flight this->CheckIfNeedExtraCheck(pBullet); return; } @@ -525,68 +516,62 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C } case ParabolaFireMode::SpeedAndAngle: // Fixed horizontal speed and fixed fire angle { - // Step 1: Read the appropriate horizontal speed - const double horizontalSpeed = this->GetTrajectorySpeed(pBullet); + // Step 1: Calculate the time when the projectile meets the target directly using horizontal velocity + const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, this->Speed); - // Step 2: Calculate the time when the projectile meets the target directly using horizontal velocity - const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, horizontalSpeed); - - // Step 3: Substitute the time into the calculation of the attack coordinates + // Step 2: Substitute the time into the calculation of the attack coordinates pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; - // Step 4: Check if it is an unsolvable solution + // Step 3: Check if it is an unsolvable solution if (meetTime <= 1e-10 || destinationCoords.Magnitude() <= 1e-10) break; - // Step 5: Calculate the ratio of horizontal velocity to horizontal distance + // Step 4: Calculate the ratio of horizontal velocity to horizontal distance const double horizontalDistance = Point2D{ destinationCoords.X, destinationCoords.Y }.Magnitude(); - const double mult = horizontalDistance > 1e-10 ? horizontalSpeed / horizontalDistance : 1.0; + const double mult = horizontalDistance > 1e-10 ? this->Speed / horizontalDistance : 1.0; - // Step 6: Calculate each horizontal component of the projectile velocity + // Step 5: Calculate each horizontal component of the projectile velocity pBullet->Velocity.X = destinationCoords.X * mult; pBullet->Velocity.Y = destinationCoords.Y * mult; - // Step 7: Calculate whole horizontal component of the projectile velocity + // Step 6: Calculate whole horizontal component of the projectile velocity const double horizontalVelocity = horizontalDistance * mult; - // Step 8: Read the appropriate fire angle + // Step 7: Read the appropriate fire angle double radian = pType->LaunchAngle * Math::Pi / 180.0; radian = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? (Math::HalfPi / 3) : radian; - // Step 9: Calculate the vertical component of the projectile velocity + // Step 8: Calculate the vertical component of the projectile velocity pBullet->Velocity.Z = horizontalVelocity * Math::tan(radian) + gravity / 2; - // Step 10: Record whether it requires additional checks during the flight + // Step 9: Record whether it requires additional checks during the flight this->CheckIfNeedExtraCheck(pBullet); return; } default: // Fixed horizontal speed and aim at the target { - // Step 1: Read the appropriate horizontal speed - const double horizontalSpeed = this->GetTrajectorySpeed(pBullet); - - // Step 2: Calculate the time when the projectile meets the target directly using horizontal velocity - const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, horizontalSpeed); + // Step 1: Calculate the time when the projectile meets the target directly using horizontal velocity + const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, this->Speed); - // Step 3: Substitute the time into the calculation of the attack coordinates + // Step 2: Substitute the time into the calculation of the attack coordinates pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; - // Step 4: Check if it is an unsolvable solution + // Step 3: Check if it is an unsolvable solution if (meetTime <= 1e-10 || destinationCoords.Magnitude() <= 1e-10) break; - // Step 5: Calculate the ratio of horizontal velocity to horizontal distance + // Step 4: Calculate the ratio of horizontal velocity to horizontal distance const double horizontalDistance = Point2D{ destinationCoords.X, destinationCoords.Y }.Magnitude(); - const double mult = horizontalDistance > 1e-10 ? horizontalSpeed / horizontalDistance : 1.0; + const double mult = horizontalDistance > 1e-10 ? this->Speed / horizontalDistance : 1.0; - // Step 6: Calculate the projectile velocity + // Step 5: Calculate the projectile velocity pBullet->Velocity.X = destinationCoords.X * mult; pBullet->Velocity.Y = destinationCoords.Y * mult; - pBullet->Velocity.Z = destinationCoords.Z * mult + (gravity * horizontalDistance) / (2 * horizontalSpeed) + gravity / 2; + pBullet->Velocity.Z = destinationCoords.Z * mult + (gravity * horizontalDistance) / (2 * this->Speed) + gravity / 2; - // Step 7: Record whether it requires additional checks during the flight + // Step 6: Record whether it requires additional checks during the flight this->CheckIfNeedExtraCheck(pBullet); return; } @@ -601,7 +586,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, CoordStruct* pSourceCoords, double gravity) { - ParabolaTrajectoryType* const pType = this->Type; + const ParabolaTrajectoryType* const pType = this->Type; // Calculate horizontal distance const CoordStruct distanceCoords = pBullet->TargetCoords - *pSourceCoords; const double distance = distanceCoords.Magnitude(); @@ -661,13 +646,10 @@ void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, C // Step 2: Calculate the vertical component of the projectile velocity pBullet->Velocity.Z = sqrt(2 * gravity * (maxHeight - sourceHeight)); - // Step 3: Read the appropriate horizontal speed - const double horizontalSpeed = this->GetTrajectorySpeed(pBullet); - - // Step 4: Calculate the ratio of horizontal velocity to horizontal distance - const double mult = horizontalDistance > 1e-10 ? horizontalSpeed / horizontalDistance : 1.0; + // Step 3: Calculate the ratio of horizontal velocity to horizontal distance + const double mult = horizontalDistance > 1e-10 ? this->Speed / horizontalDistance : 1.0; - // Step 5: Calculate the horizontal component of the projectile velocity + // Step 4: Calculate the horizontal component of the projectile velocity pBullet->Velocity.X = distanceCoords.X * mult; pBullet->Velocity.Y = distanceCoords.Y * mult; break; @@ -695,36 +677,30 @@ void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, C } case ParabolaFireMode::SpeedAndAngle: // Fixed horizontal speed and fixed fire angle { - // Step 1: Read the appropriate horizontal speed - const double horizontalSpeed = this->GetTrajectorySpeed(pBullet); + // Step 1: Calculate the ratio of horizontal velocity to horizontal distance + const double mult = horizontalDistance > 1e-10 ? this->Speed / horizontalDistance : 1.0; - // Step 2: Calculate the ratio of horizontal velocity to horizontal distance - const double mult = horizontalDistance > 1e-10 ? horizontalSpeed / horizontalDistance : 1.0; - - // Step 3: Calculate the horizontal component of the projectile velocity + // Step 2: Calculate the horizontal component of the projectile velocity pBullet->Velocity.X = distanceCoords.X * mult; pBullet->Velocity.Y = distanceCoords.Y * mult; - // Step 4: Read the appropriate fire angle + // Step 3: Read the appropriate fire angle double radian = pType->LaunchAngle * Math::Pi / 180.0; radian = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? (Math::HalfPi / 3) : radian; - // Step 5: Calculate the vertical component of the projectile velocity - pBullet->Velocity.Z = horizontalSpeed * Math::tan(radian); + // Step 4: Calculate the vertical component of the projectile velocity + pBullet->Velocity.Z = this->Speed * Math::tan(radian); break; } default: // Fixed horizontal speed and aim at the target { - // Step 1: Read the appropriate horizontal speed - const double horizontalSpeed = this->GetTrajectorySpeed(pBullet); - - // Step 2: Calculate the ratio of horizontal velocity to horizontal distance - const double mult = horizontalDistance > 1e-10 ? horizontalSpeed / horizontalDistance : 1.0; + // Step 1: Calculate the ratio of horizontal velocity to horizontal distance + const double mult = horizontalDistance > 1e-10 ? this->Speed / horizontalDistance : 1.0; - // Step 3: Calculate the projectile velocity + // Step 2: Calculate the projectile velocity pBullet->Velocity.X = distanceCoords.X * mult; pBullet->Velocity.Y = distanceCoords.Y * mult; - pBullet->Velocity.Z = distanceCoords.Z * mult + (gravity * horizontalDistance) / (2 * horizontalSpeed); + pBullet->Velocity.Z = distanceCoords.Z * mult + (gravity * horizontalDistance) / (2 * this->Speed); break; } } @@ -749,7 +725,7 @@ void ParabolaTrajectory::CheckIfNeedExtraCheck(BulletClass* pBullet) } default: // Fixed horizontal speed and blabla { - this->NeedExtraCheck = this->GetTrajectorySpeed(pBullet) > 256.0; + this->NeedExtraCheck = this->Speed > 256.0; break; } } @@ -939,7 +915,7 @@ bool ParabolaTrajectory::CalculateBulletVelocityAfterBounce(BulletClass* pBullet --this->BounceTimes; this->ShouldBounce = false; - ParabolaTrajectoryType* const pType = this->Type; + const ParabolaTrajectoryType* const pType = this->Type; const BulletVelocity groundNormalVector = this->GetGroundNormalVector(pBullet, pCell); pBullet->Velocity = (this->LastVelocity - groundNormalVector * (this->LastVelocity * groundNormalVector) * 2) * pType->BounceCoefficient; pBullet->Velocity.Z -= gravity; @@ -1088,7 +1064,7 @@ bool ParabolaTrajectory::BulletDetonatePreCheck(BulletClass* pBullet) if (this->ShouldDetonate) return true; - ParabolaTrajectoryType* const pType = this->Type; + const ParabolaTrajectoryType* const pType = this->Type; if (pType->DetonationHeight >= 0 && pBullet->Velocity.Z < 1e-10 && (pBullet->Location.Z - pBullet->SourceCoords.Z) < pType->DetonationHeight) return true; diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h index 2a6040340a..0deacb8e8b 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h @@ -40,7 +40,7 @@ class ParabolaTrajectoryType final : public PhobosTrajectoryType virtual bool Load(PhobosStreamReader& Stm, bool RegisterForChange) override; virtual bool Save(PhobosStreamWriter& Stm) const override; - virtual PhobosTrajectory* CreateInstance() const override; + virtual std::unique_ptr CreateInstance() const override; virtual void Read(CCINIClass* const pINI, const char* pSection) override; Valueable DetonationDistance; @@ -74,12 +74,12 @@ class ParabolaTrajectory final : public PhobosTrajectory public: ParabolaTrajectory(noinit_t) :PhobosTrajectory { noinit_t{} } { } - ParabolaTrajectory(PhobosTrajectoryType const* pType) : PhobosTrajectory(TrajectoryFlag::Parabola) - , Type { static_cast(const_cast(pType)) } - , ThrowHeight { Type->ThrowHeight > 0 ? Type->ThrowHeight : 600 } - , BounceTimes { Type->BounceTimes } - , OffsetCoord { static_cast(Type->OffsetCoord) } - , UseDisperseBurst { Type->UseDisperseBurst } + ParabolaTrajectory(ParabolaTrajectoryType const* trajType) : PhobosTrajectory(TrajectoryFlag::Parabola, trajType->Trajectory_Speed) + , Type { trajType } + , ThrowHeight { trajType->ThrowHeight > 0 ? trajType->ThrowHeight : 600 } + , BounceTimes { trajType->BounceTimes } + , OffsetCoord { trajType->OffsetCoord.Get() } + , UseDisperseBurst { trajType->UseDisperseBurst } , ShouldDetonate { false } , ShouldBounce { false } , NeedExtraCheck { false } @@ -100,7 +100,7 @@ class ParabolaTrajectory final : public PhobosTrajectory virtual TrajectoryCheckReturnType OnAITargetCoordCheck(BulletClass* pBullet) override; virtual TrajectoryCheckReturnType OnAITechnoCheck(BulletClass* pBullet, TechnoClass* pTechno) override; - ParabolaTrajectoryType* Type; + const ParabolaTrajectoryType* Type; int ThrowHeight; int BounceTimes; CoordStruct OffsetCoord; diff --git a/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp b/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp index 0051aa9bdd..3f237fca5c 100644 --- a/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp @@ -7,10 +7,7 @@ #include "StraightTrajectory.h" #include "BombardTrajectory.h" -#include "DisperseTrajectory.h" -#include "EngraveTrajectory.h" #include "ParabolaTrajectory.h" -#include "TracingTrajectory.h" TrajectoryTypePointer::TrajectoryTypePointer(TrajectoryFlag flag) { @@ -22,18 +19,9 @@ TrajectoryTypePointer::TrajectoryTypePointer(TrajectoryFlag flag) case TrajectoryFlag::Bombard: _ptr = std::make_unique(); return; - case TrajectoryFlag::Disperse: - _ptr = std::make_unique(); - return; - case TrajectoryFlag::Engrave: - _ptr = std::make_unique(); - return; case TrajectoryFlag::Parabola: _ptr = std::make_unique(); return; - case TrajectoryFlag::Tracing: - _ptr = std::make_unique(); - return; } _ptr.reset(); } @@ -49,10 +37,7 @@ namespace detail { {"Straight", TrajectoryFlag::Straight}, {"Bombard" ,TrajectoryFlag::Bombard}, - {"Disperse", TrajectoryFlag::Disperse}, - {"Engrave" ,TrajectoryFlag::Engrave}, {"Parabola", TrajectoryFlag::Parabola}, - {"Tracing" ,TrajectoryFlag::Tracing}, }; for (auto [name, flag] : FlagNames) { @@ -133,18 +118,9 @@ bool TrajectoryPointer::Load(PhobosStreamReader& Stm, bool registerForChange) case TrajectoryFlag::Bombard: _ptr = std::make_unique(noinit_t {}); break; - case TrajectoryFlag::Disperse: - _ptr = std::make_unique(noinit_t {}); - break; - case TrajectoryFlag::Engrave: - _ptr = std::make_unique(noinit_t {}); - break; case TrajectoryFlag::Parabola: _ptr = std::make_unique(noinit_t {}); break; - case TrajectoryFlag::Tracing: - _ptr = std::make_unique(noinit_t {}); - break; default: _ptr.reset(); break; From 0961d8daf8555da9b22d91fd76f0e68239589996 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Fri, 4 Oct 2024 18:40:13 +0800 Subject: [PATCH 14/32] Fix merge --- src/Ext/Bullet/Hooks.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ext/Bullet/Hooks.cpp b/src/Ext/Bullet/Hooks.cpp index 664e9c3e3b..2682aad85c 100644 --- a/src/Ext/Bullet/Hooks.cpp +++ b/src/Ext/Bullet/Hooks.cpp @@ -305,7 +305,7 @@ DEFINE_HOOK(0x467CCA, BulletClass_AI_TargetSnapChecks, 0x6) } else if (auto const pExt = BulletAITemp::ExtData) { - if (auto const pTrajectory = pExt->Trajectory) + if (auto const pTrajectory = pExt->Trajectory.get()) { const TrajectoryFlag flag = pTrajectory->Flag; @@ -340,7 +340,7 @@ DEFINE_HOOK(0x468E61, BulletClass_Explode_TargetSnapChecks1, 0x6) { if (!pExt->SnappedToTarget) { - if (auto const pTrajectory = pExt->Trajectory) + if (auto const pTrajectory = pExt->Trajectory.get()) { const TrajectoryFlag flag = pTrajectory->Flag; @@ -379,7 +379,7 @@ DEFINE_HOOK(0x468E9F, BulletClass_Explode_TargetSnapChecks2, 0x6) { if (!pExt->SnappedToTarget) { - if (auto const pTrajectory = pExt->Trajectory) + if (auto const pTrajectory = pExt->Trajectory.get()) { const TrajectoryFlag flag = pTrajectory->Flag; @@ -402,7 +402,7 @@ DEFINE_HOOK(0x468D3F, BulletClass_ShouldExplode_AirTarget, 0x6) if (auto const pExt = BulletExt::ExtMap.Find(pThis)) { - if (auto const pTrajectory = pExt->Trajectory) + if (auto const pTrajectory = pExt->Trajectory.get()) { const TrajectoryFlag flag = pTrajectory->Flag; From 2cfea20875b275bc4ec01bd2b2b28a18a01e84bb Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Fri, 4 Oct 2024 20:57:52 +0800 Subject: [PATCH 15/32] Fix merge --- src/Ext/Bullet/Trajectories/ParabolaTrajectory.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h index 0deacb8e8b..08366768f9 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h @@ -15,7 +15,7 @@ enum class ParabolaFireMode class ParabolaTrajectoryType final : public PhobosTrajectoryType { public: - ParabolaTrajectoryType() : PhobosTrajectoryType(TrajectoryFlag::Parabola) + ParabolaTrajectoryType() : PhobosTrajectoryType() , DetonationDistance { Leptons(102) } , TargetSnapDistance { Leptons(128) } , OpenFireMode { ParabolaFireMode::Speed } @@ -42,6 +42,7 @@ class ParabolaTrajectoryType final : public PhobosTrajectoryType virtual bool Save(PhobosStreamWriter& Stm) const override; virtual std::unique_ptr CreateInstance() const override; virtual void Read(CCINIClass* const pINI, const char* pSection) override; + virtual TrajectoryFlag Flag() const { return TrajectoryFlag::Parabola; } Valueable DetonationDistance; Valueable TargetSnapDistance; @@ -74,7 +75,7 @@ class ParabolaTrajectory final : public PhobosTrajectory public: ParabolaTrajectory(noinit_t) :PhobosTrajectory { noinit_t{} } { } - ParabolaTrajectory(ParabolaTrajectoryType const* trajType) : PhobosTrajectory(TrajectoryFlag::Parabola, trajType->Trajectory_Speed) + ParabolaTrajectory(ParabolaTrajectoryType const* trajType) : PhobosTrajectory(trajType->Trajectory_Speed) , Type { trajType } , ThrowHeight { trajType->ThrowHeight > 0 ? trajType->ThrowHeight : 600 } , BounceTimes { trajType->BounceTimes } @@ -92,7 +93,7 @@ class ParabolaTrajectory final : public PhobosTrajectory virtual bool Load(PhobosStreamReader& Stm, bool RegisterForChange) override; virtual bool Save(PhobosStreamWriter& Stm) const override; - + virtual TrajectoryFlag Flag() const { return TrajectoryFlag::Parabola; } virtual void OnUnlimbo(BulletClass* pBullet, CoordStruct* pCoord, BulletVelocity* pVelocity) override; virtual bool OnAI(BulletClass* pBullet) override; virtual void OnAIPreDetonate(BulletClass* pBullet) override; From a3a79ce7bca257f860821588851b09635af733c6 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Sun, 6 Oct 2024 03:49:49 +0800 Subject: [PATCH 16/32] Reduce the error during long-distance attacks in Angle Mode to improve accuracy --- src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp index 135f76503c..c42671e56e 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp @@ -737,10 +737,11 @@ double ParabolaTrajectory::SearchVelocity(double horizontalDistance, int distanc const double mult = Math::sin(2 * radian); double velocity = abs(mult) > 1e-10 ? sqrt(horizontalDistance * gravity / mult) : 0.0; velocity += distanceCoordsZ / gravity; - velocity = velocity > 10.0 ? velocity : 10.0; + velocity = velocity > 8.0 ? velocity : 8.0; + const double error = velocity / 16; // Step size - const double delta = 1e-6; + const double delta = 1e-5; // Newton Iteration Method for (int i = 0; i < 10; ++i) @@ -758,7 +759,7 @@ double ParabolaTrajectory::SearchVelocity(double horizontalDistance, int distanc const double velocityNew = velocity - difference; // Check tolerable error - if (abs(difference) < 8.0) + if (abs(difference) < error) return velocityNew; // Update the speed From 3486a5c1b7f47dd3777c567b55d6c298a29ed6ec Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Sun, 6 Oct 2024 16:04:10 +0800 Subject: [PATCH 17/32] Improve inspection accuracy --- .../Trajectories/ParabolaTrajectory.cpp | 76 +++++++++---------- .../Bullet/Trajectories/ParabolaTrajectory.h | 4 +- 2 files changed, 39 insertions(+), 41 deletions(-) diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp index c42671e56e..94e2ac8fe4 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp @@ -170,41 +170,36 @@ bool ParabolaTrajectory::OnAI(BulletClass* pBullet) return true; CellClass* const pCell = MapClass::Instance->TryGetCellAt(pBullet->Location); + const bool bounce = this->ShouldBounce; - if (!pCell) + if (!pCell || (bounce && this->CalculateBulletVelocityAfterBounce(pBullet, pCell))) return true; - const double gravity = BulletTypeExt::GetAdjustedGravity(pBullet->Type); - - if (this->ShouldBounce && this->BounceTimes > 0) - return (pCell->LandType == LandType::Water && !this->Type->BounceOnWater) || this->CalculateBulletVelocityAfterBounce(pBullet, pCell, gravity); - - return this->BulletDetonateLastCheck(pBullet, gravity); + return this->BulletDetonateLastCheck(pBullet, pCell, BulletTypeExt::GetAdjustedGravity(pBullet->Type), bounce); } void ParabolaTrajectory::OnAIPreDetonate(BulletClass* pBullet) { const Leptons targetSnapDistance = this->Type->TargetSnapDistance; - if (targetSnapDistance <= 0) - return; - - const ObjectClass* const pTarget = abstract_cast(pBullet->Target); - const CoordStruct coords = pTarget ? pTarget->GetCoords() : pBullet->Data.Location; - - if (coords.DistanceFrom(pBullet->Location) <= targetSnapDistance) + if (targetSnapDistance > 0) { - auto const pExt = BulletExt::ExtMap.Find(pBullet); - pExt->SnappedToTarget = true; - pBullet->SetLocation(coords); - } - else - { - const int cellHeight = MapClass::Instance->GetCellFloorHeight(pBullet->Location); + const ObjectClass* const pTarget = abstract_cast(pBullet->Target); + const CoordStruct coords = pTarget ? pTarget->GetCoords() : pBullet->Data.Location; - if (pBullet->Location.Z < cellHeight) - pBullet->SetLocation(CoordStruct{ pBullet->Location.X, pBullet->Location.Y, cellHeight }); + if (coords.DistanceFrom(pBullet->Location) <= targetSnapDistance) + { + auto const pExt = BulletExt::ExtMap.Find(pBullet); + pExt->SnappedToTarget = true; + pBullet->SetLocation(coords); + return; + } } + + const int cellHeight = MapClass::Instance->GetCellFloorHeight(pBullet->Location); + + if (pBullet->Location.Z < cellHeight) + pBullet->SetLocation(CoordStruct{ pBullet->Location.X, pBullet->Location.Y, cellHeight }); } void ParabolaTrajectory::OnAIVelocity(BulletClass* pBullet, BulletVelocity* pSpeed, BulletVelocity* pPosition) @@ -911,15 +906,18 @@ double ParabolaTrajectory::CheckFixedAngleEquation(CoordStruct* pSourceCrd, Coor return upTime + downTime - meetTime; } -bool ParabolaTrajectory::CalculateBulletVelocityAfterBounce(BulletClass* pBullet, CellClass* pCell, double gravity) +bool ParabolaTrajectory::CalculateBulletVelocityAfterBounce(BulletClass* pBullet, CellClass* pCell) { + const ParabolaTrajectoryType* const pType = this->Type; + + if (pCell->LandType == LandType::Water && !pType->BounceOnWater) + return true; + --this->BounceTimes; this->ShouldBounce = false; - const ParabolaTrajectoryType* const pType = this->Type; const BulletVelocity groundNormalVector = this->GetGroundNormalVector(pBullet, pCell); pBullet->Velocity = (this->LastVelocity - groundNormalVector * (this->LastVelocity * groundNormalVector) * 2) * pType->BounceCoefficient; - pBullet->Velocity.Z -= gravity; if (pType->BounceDetonate) { @@ -1096,7 +1094,7 @@ bool ParabolaTrajectory::BulletDetonatePreCheck(BulletClass* pBullet) return false; } -bool ParabolaTrajectory::BulletDetonateLastCheck(BulletClass* pBullet, double gravity) +bool ParabolaTrajectory::BulletDetonateLastCheck(BulletClass* pBullet, CellClass* pCell, double gravity, bool bounce) { pBullet->Velocity.Z -= gravity; @@ -1105,30 +1103,27 @@ bool ParabolaTrajectory::BulletDetonateLastCheck(BulletClass* pBullet, double gr if (this->NeedExtraCheck) { - const CellStruct sourceCell = CellClass::Coord2Cell(pBullet->Location); - const CellStruct targetCell = CellClass::Coord2Cell(futureCoords); - const CellStruct cellDist = sourceCell - targetCell; + const CellStruct cellDist = CellClass::Coord2Cell(pBullet->Location) - CellClass::Coord2Cell(futureCoords); const CellStruct cellPace = CellStruct { static_cast(std::abs(cellDist.X)), static_cast(std::abs(cellDist.Y)) }; - const size_t largePace = static_cast(std::max(cellPace.X, cellPace.Y)); const CoordStruct stepCoord = largePace ? velocityCoords * (1.0 / largePace) : CoordStruct::Empty; - CoordStruct curCoord = pBullet->Location; - CellClass* pCurCell = MapClass::Instance->GetCellAt(sourceCell); + CoordStruct curCoord = pBullet->Location + stepCoord; - for (size_t i = 0; i < largePace; ++i) + for (size_t i = 1; i <= largePace; ++i) { const int cellHeight = MapClass::Instance->GetCellFloorHeight(curCoord); if (curCoord.Z < cellHeight) { + if (bounce) + return true; + this->LastVelocity = pBullet->Velocity; - const double heightMult = abs((pBullet->Location.Z - cellHeight) / pBullet->Velocity.Z); - const double speedMult = static_cast(i) / largePace; - this->BulletDetonateEffectuate(pBullet, (heightMult < speedMult ? heightMult : speedMult)); + this->BulletDetonateEffectuate(pBullet, (static_cast(i - 0.5) / largePace)); break; } - if (pBullet->Type->SubjectToWalls && pCurCell->OverlayTypeIndex != -1 && OverlayTypeClass::Array->GetItem(pCurCell->OverlayTypeIndex)->Wall) + if (pBullet->Type->SubjectToWalls && pCell->OverlayTypeIndex != -1 && OverlayTypeClass::Array->GetItem(pCell->OverlayTypeIndex)->Wall) { pBullet->Velocity *= static_cast(i) / largePace; this->ShouldDetonate = true; @@ -1136,7 +1131,7 @@ bool ParabolaTrajectory::BulletDetonateLastCheck(BulletClass* pBullet, double gr } curCoord += stepCoord; - pCurCell = MapClass::Instance->GetCellAt(curCoord); + pCell = MapClass::Instance->GetCellAt(curCoord); } } else @@ -1146,6 +1141,9 @@ bool ParabolaTrajectory::BulletDetonateLastCheck(BulletClass* pBullet, double gr if (cellHeight < futureCoords.Z) return false; + if (bounce) + return true; + this->LastVelocity = pBullet->Velocity; this->BulletDetonateEffectuate(pBullet, abs((pBullet->Location.Z - cellHeight) / pBullet->Velocity.Z)); } diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h index 08366768f9..7d2529245a 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h @@ -131,10 +131,10 @@ class ParabolaTrajectory final : public PhobosTrajectory double CheckFixedHeightEquation(CoordStruct* pSourceCrd, CoordStruct* pTargetCrd, CoordStruct* pOffsetCrd, double meetTime, double gravity); double SearchFixedAngleMeetTime(CoordStruct* pSourceCrd, CoordStruct* pTargetCrd, CoordStruct* pOffsetCrd, double radian, double gravity); double CheckFixedAngleEquation(CoordStruct* pSourceCrd, CoordStruct* pTargetCrd, CoordStruct* pOffsetCrd, double meetTime, double radian, double gravity); - bool CalculateBulletVelocityAfterBounce(BulletClass* pBullet, CellClass* pCell, double gravity); + bool CalculateBulletVelocityAfterBounce(BulletClass* pBullet, CellClass* pCell); BulletVelocity GetGroundNormalVector(BulletClass* pBullet, CellClass* pCell); bool CheckBulletHitCliff(short X, short Y, int bulletHeight, int lastCellHeight); bool BulletDetonatePreCheck(BulletClass* pBullet); - bool BulletDetonateLastCheck(BulletClass* pBullet, double gravity); + bool BulletDetonateLastCheck(BulletClass* pBullet, CellClass* pCell, double gravity, bool bounce); void BulletDetonateEffectuate(BulletClass* pBullet, double velocityMult); }; From 71abf6ec0f46adb7760d684264af31f262741b41 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Sun, 6 Oct 2024 20:43:26 +0800 Subject: [PATCH 18/32] Fix merge --- .../Trajectories/ParabolaTrajectory.cpp | 44 +++++++++---------- .../Bullet/Trajectories/ParabolaTrajectory.h | 13 +++--- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp index 94e2ac8fe4..4a605c0a17 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp @@ -114,14 +114,12 @@ void ParabolaTrajectory::Serialize(T& Stm) bool ParabolaTrajectory::Load(PhobosStreamReader& Stm, bool RegisterForChange) { - this->PhobosTrajectory::Load(Stm, false); this->Serialize(Stm); return true; } bool ParabolaTrajectory::Save(PhobosStreamWriter& Stm) const { - this->PhobosTrajectory::Save(Stm); const_cast(this)->Serialize(Stm); return true; } @@ -360,7 +358,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C default: { // Assuming equal height - leadTime = static_cast((Unsorted::LeptonsPerCell << 2) / this->Speed); + leadTime = static_cast((Unsorted::LeptonsPerCell << 2) / pType->Trajectory_Speed); break; } } @@ -444,7 +442,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C case ParabolaFireMode::SpeedAndHeight: // Fixed horizontal speed and fixed max height { // Step 1: Calculate the time when the projectile meets the target directly using horizontal velocity - const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, this->Speed); + const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, pType->Trajectory_Speed); // Step 2: Substitute the time into the calculation of the attack coordinates pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); @@ -456,7 +454,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C // Step 4: Calculate the ratio of horizontal velocity to horizontal distance const double horizontalDistance = Point2D{ destinationCoords.X, destinationCoords.Y }.Magnitude(); - const double mult = horizontalDistance > 1e-10 ? this->Speed / horizontalDistance : 1.0; + const double mult = horizontalDistance > 1e-10 ? pType->Trajectory_Speed / horizontalDistance : 1.0; // Step 5: Calculate the horizontal component of the projectile velocity pBullet->Velocity.X = destinationCoords.X * mult; @@ -512,7 +510,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C case ParabolaFireMode::SpeedAndAngle: // Fixed horizontal speed and fixed fire angle { // Step 1: Calculate the time when the projectile meets the target directly using horizontal velocity - const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, this->Speed); + const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, pType->Trajectory_Speed); // Step 2: Substitute the time into the calculation of the attack coordinates pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); @@ -524,7 +522,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C // Step 4: Calculate the ratio of horizontal velocity to horizontal distance const double horizontalDistance = Point2D{ destinationCoords.X, destinationCoords.Y }.Magnitude(); - const double mult = horizontalDistance > 1e-10 ? this->Speed / horizontalDistance : 1.0; + const double mult = horizontalDistance > 1e-10 ? pType->Trajectory_Speed / horizontalDistance : 1.0; // Step 5: Calculate each horizontal component of the projectile velocity pBullet->Velocity.X = destinationCoords.X * mult; @@ -547,7 +545,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C default: // Fixed horizontal speed and aim at the target { // Step 1: Calculate the time when the projectile meets the target directly using horizontal velocity - const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, this->Speed); + const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, pType->Trajectory_Speed); // Step 2: Substitute the time into the calculation of the attack coordinates pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); @@ -559,12 +557,12 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C // Step 4: Calculate the ratio of horizontal velocity to horizontal distance const double horizontalDistance = Point2D{ destinationCoords.X, destinationCoords.Y }.Magnitude(); - const double mult = horizontalDistance > 1e-10 ? this->Speed / horizontalDistance : 1.0; + const double mult = horizontalDistance > 1e-10 ? pType->Trajectory_Speed / horizontalDistance : 1.0; // Step 5: Calculate the projectile velocity pBullet->Velocity.X = destinationCoords.X * mult; pBullet->Velocity.Y = destinationCoords.Y * mult; - pBullet->Velocity.Z = destinationCoords.Z * mult + (gravity * horizontalDistance) / (2 * this->Speed) + gravity / 2; + pBullet->Velocity.Z = destinationCoords.Z * mult + (gravity * horizontalDistance) / (2 * pType->Trajectory_Speed) + gravity / 2; // Step 6: Record whether it requires additional checks during the flight this->CheckIfNeedExtraCheck(pBullet); @@ -642,7 +640,7 @@ void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, C pBullet->Velocity.Z = sqrt(2 * gravity * (maxHeight - sourceHeight)); // Step 3: Calculate the ratio of horizontal velocity to horizontal distance - const double mult = horizontalDistance > 1e-10 ? this->Speed / horizontalDistance : 1.0; + const double mult = horizontalDistance > 1e-10 ? pType->Trajectory_Speed / horizontalDistance : 1.0; // Step 4: Calculate the horizontal component of the projectile velocity pBullet->Velocity.X = distanceCoords.X * mult; @@ -673,7 +671,7 @@ void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, C case ParabolaFireMode::SpeedAndAngle: // Fixed horizontal speed and fixed fire angle { // Step 1: Calculate the ratio of horizontal velocity to horizontal distance - const double mult = horizontalDistance > 1e-10 ? this->Speed / horizontalDistance : 1.0; + const double mult = horizontalDistance > 1e-10 ? pType->Trajectory_Speed / horizontalDistance : 1.0; // Step 2: Calculate the horizontal component of the projectile velocity pBullet->Velocity.X = distanceCoords.X * mult; @@ -684,18 +682,18 @@ void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, C radian = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? (Math::HalfPi / 3) : radian; // Step 4: Calculate the vertical component of the projectile velocity - pBullet->Velocity.Z = this->Speed * Math::tan(radian); + pBullet->Velocity.Z = pType->Trajectory_Speed * Math::tan(radian); break; } default: // Fixed horizontal speed and aim at the target { // Step 1: Calculate the ratio of horizontal velocity to horizontal distance - const double mult = horizontalDistance > 1e-10 ? this->Speed / horizontalDistance : 1.0; + const double mult = horizontalDistance > 1e-10 ? pType->Trajectory_Speed / horizontalDistance : 1.0; // Step 2: Calculate the projectile velocity pBullet->Velocity.X = distanceCoords.X * mult; pBullet->Velocity.Y = distanceCoords.Y * mult; - pBullet->Velocity.Z = distanceCoords.Z * mult + (gravity * horizontalDistance) / (2 * this->Speed); + pBullet->Velocity.Z = distanceCoords.Z * mult + (gravity * horizontalDistance) / (2 * pType->Trajectory_Speed); break; } } @@ -709,7 +707,9 @@ void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, C void ParabolaTrajectory::CheckIfNeedExtraCheck(BulletClass* pBullet) { - switch (this->Type->OpenFireMode) + const ParabolaTrajectoryType* const pType = this->Type; + + switch (pType->OpenFireMode) { case ParabolaFireMode::Height: // Fixed max height and aim at the target case ParabolaFireMode::Angle: // Fixed fire angle and aim at the target @@ -720,7 +720,7 @@ void ParabolaTrajectory::CheckIfNeedExtraCheck(BulletClass* pBullet) } default: // Fixed horizontal speed and blabla { - this->NeedExtraCheck = this->Speed > 256.0; + this->NeedExtraCheck = pType->Trajectory_Speed > 256.0; break; } } @@ -992,22 +992,22 @@ BulletVelocity ParabolaTrajectory::GetGroundNormalVector(BulletClass* pBullet, C const BulletVelocity velocity = horizontalVelocity > 362.1 ? pBullet->Velocity * (362.1 / horizontalVelocity) : pBullet->Velocity; const CoordStruct velocityCoords { static_cast(velocity.X), static_cast(velocity.Y), static_cast(velocity.Z) }; - const int cellHeight = pCell->GetCoords().Z; + const int cellHeight = pCell->Level * Unsorted::LevelHeight; const int bulletHeight = pBullet->Location.Z; const int lastCellHeight = MapClass::Instance->GetCellFloorHeight(pBullet->Location - velocityCoords); if (bulletHeight < cellHeight && (cellHeight - lastCellHeight) > 384) { CellStruct cell = pCell->MapCoords; - const short reverseSgnX = pBullet->Velocity.X > -(1e-10) ? -1 : 1; - const short reverseSgnY = pBullet->Velocity.Y > -(1e-10) ? -1 : 1; + const short reverseSgnX = pBullet->Velocity.X > 0.0 ? -1 : 1; + const short reverseSgnY = pBullet->Velocity.Y > 0.0 ? -1 : 1; int index = 0; if (this->CheckBulletHitCliff(cell.X + reverseSgnX, cell.Y, bulletHeight, lastCellHeight)) { if (!this->CheckBulletHitCliff(cell.X, cell.Y + reverseSgnY, bulletHeight, lastCellHeight)) { - if (!this->CheckBulletHitCliff(cell.X - reverseSgnX, cell.Y, bulletHeight, lastCellHeight)) + if (!this->CheckBulletHitCliff(cell.X - reverseSgnX, cell.Y + reverseSgnY, bulletHeight, lastCellHeight)) return BulletVelocity{ 0.0, static_cast(reverseSgnY), 0.0 }; index = 2; @@ -1049,7 +1049,7 @@ bool ParabolaTrajectory::CheckBulletHitCliff(short X, short Y, int bulletHeight, { if (CellClass* const pCell = MapClass::Instance->TryGetCellAt(CellStruct{ X, Y })) { - const int cellHeight = pCell->GetCoords().Z; + const int cellHeight = pCell->Level * Unsorted::LevelHeight; if (bulletHeight < cellHeight && (cellHeight - lastCellHeight) > 384) return true; diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h index 7d2529245a..ed2d2f32d0 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h @@ -36,13 +36,13 @@ class ParabolaTrajectoryType final : public PhobosTrajectoryType , MirrorCoord { true } , UseDisperseBurst { false } , AxisOfRotation { { 0, 0, 1 } } - {} + { } virtual bool Load(PhobosStreamReader& Stm, bool RegisterForChange) override; virtual bool Save(PhobosStreamWriter& Stm) const override; virtual std::unique_ptr CreateInstance() const override; virtual void Read(CCINIClass* const pINI, const char* pSection) override; - virtual TrajectoryFlag Flag() const { return TrajectoryFlag::Parabola; } + virtual TrajectoryFlag Flag() const override { return TrajectoryFlag::Parabola; } Valueable DetonationDistance; Valueable TargetSnapDistance; @@ -73,10 +73,9 @@ class ParabolaTrajectoryType final : public PhobosTrajectoryType class ParabolaTrajectory final : public PhobosTrajectory { public: - ParabolaTrajectory(noinit_t) :PhobosTrajectory { noinit_t{} } { } + ParabolaTrajectory(noinit_t) { } - ParabolaTrajectory(ParabolaTrajectoryType const* trajType) : PhobosTrajectory(trajType->Trajectory_Speed) - , Type { trajType } + ParabolaTrajectory(ParabolaTrajectoryType const* trajType) : Type { trajType } , ThrowHeight { trajType->ThrowHeight > 0 ? trajType->ThrowHeight : 600 } , BounceTimes { trajType->BounceTimes } , OffsetCoord { trajType->OffsetCoord.Get() } @@ -89,11 +88,11 @@ class ParabolaTrajectory final : public PhobosTrajectory , CountOfBurst { 0 } , WaitOneFrame {} , LastVelocity {} - {} + { } virtual bool Load(PhobosStreamReader& Stm, bool RegisterForChange) override; virtual bool Save(PhobosStreamWriter& Stm) const override; - virtual TrajectoryFlag Flag() const { return TrajectoryFlag::Parabola; } + virtual TrajectoryFlag Flag() const override { return TrajectoryFlag::Parabola; } virtual void OnUnlimbo(BulletClass* pBullet, CoordStruct* pCoord, BulletVelocity* pVelocity) override; virtual bool OnAI(BulletClass* pBullet) override; virtual void OnAIPreDetonate(BulletClass* pBullet) override; From 6defd70b5abb2f644eaab1395b11a3c2e903ee78 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Fri, 11 Oct 2024 16:00:31 +0800 Subject: [PATCH 19/32] Use get instead of static cast --- src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp index 4a605c0a17..df2b5bc3bd 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp @@ -132,7 +132,7 @@ void ParabolaTrajectory::OnUnlimbo(BulletClass* pBullet, CoordStruct* pCoord, Bu FootClass* const pTarget = abstract_cast(pBullet->Target); bool resetTarget = false; - if (static_cast(pType->DetonationDistance) <= -1e-10 && pTarget) + if (pType->DetonationDistance.Get() <= -1e-10 && pTarget) { if (CellClass* const pCell = MapClass::Instance->TryGetCellAt(pTarget->GetCoords())) { @@ -1088,7 +1088,7 @@ bool ParabolaTrajectory::BulletDetonatePreCheck(BulletClass* pBullet) } } - if (pBullet->TargetCoords.DistanceFrom(pBullet->Location) < static_cast(pType->DetonationDistance)) + if (pBullet->TargetCoords.DistanceFrom(pBullet->Location) < pType->DetonationDistance.Get()) return true; return false; From a389b40cb71836b41cfc56bcc6e0a07b0e426a71 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Fri, 11 Oct 2024 20:29:13 +0800 Subject: [PATCH 20/32] Small fix cur burst idx --- src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp index df2b5bc3bd..7183f46a84 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp @@ -287,7 +287,7 @@ void ParabolaTrajectory::PrepareForOpenFire(BulletClass* pBullet) if (pType->MirrorCoord) { - if (pBullet->Owner && pBullet->Owner->CurrentBurstIndex % 2 == 1) + if (this->CurrentBurst % 2 == 1) rotationAxis *= -1; extraRotate = Math::Pi * (pType->RotateCoord * ((this->CurrentBurst / 2) / (this->CountOfBurst - 1.0) - 0.5)) / 180; From 7fcbd0a79bb65bdf8db30d77e658cbc89e32a8c4 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Fri, 11 Oct 2024 22:23:16 +0800 Subject: [PATCH 21/32] Small fix --- src/Ext/Bullet/Trajectories/ParabolaTrajectory.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h index ed2d2f32d0..a8b7c39824 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h @@ -60,7 +60,7 @@ class ParabolaTrajectoryType final : public PhobosTrajectoryType Valueable BounceAttenuation; Valueable BounceCoefficient; Valueable OffsetCoord; - Valueable RotateCoord; + Valueable RotateCoord; Valueable MirrorCoord; Valueable UseDisperseBurst; Valueable AxisOfRotation; From ae25e1cc334e97c3d107b7703e51fda01bc4b6e9 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Sat, 12 Oct 2024 00:20:13 +0800 Subject: [PATCH 22/32] Small fix --- src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp index 7183f46a84..bfa3562e6a 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp @@ -806,10 +806,12 @@ double ParabolaTrajectory::SolveFixedSpeedMeetTime(CoordStruct* pSourceCrd, Coor const double timeP = (-factor + sqrt(delta)) / divisor; const double timeM = (-factor - sqrt(delta)) / divisor; - if (timeM > 1e-10) + if (timeM > 1e-10 && timeP > 1e-10) + return timeM < timeP ? timeM : timeP; + else if (timeM > 1e-10) return timeM; - - return timeP; + else if (timeP > 1e-10) + return timeP; } return -1.0; From 90d256d59f508bb29bd4e722e048271f24ec86ac Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Sat, 12 Oct 2024 14:42:11 +0800 Subject: [PATCH 23/32] Fix inaccurate lead time calculate --- docs/New-or-Enhanced-Logics.md | 4 - .../Trajectories/ParabolaTrajectory.cpp | 94 +++++-------------- .../Bullet/Trajectories/ParabolaTrajectory.h | 8 +- 3 files changed, 24 insertions(+), 82 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 14647c7703..d4b4196216 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -722,8 +722,6 @@ Trajectory.Bombard.Height=0.0 ; double - `Trajectory.Parabola.ThrowHeight` controls the maximum height of the projectile and is only used for modes 1, 3, or 4. The specific height will be determined by taking the larger of the launch height and the target height then increasing this value. Non positive numbers are not supported. - `Trajectory.Parabola.LaunchAngle` controls the fire angle of the projectile and is only used for modes 2, 4, or 5. Only supports -90.0 ~ 90.0 (Cannot use boundary values) in Mode 2 or 5, and 0.0 ~ 90.0 (Cannot use boundary values) in Mode 4. - `Trajectory.Parabola.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.Parabola.LeadTimeSimplify` controls whether only perform simplified calculations when calculate the lead time. You can simply consider this as another calculation mode. - - `Trajectory.Parabola.LeadTimeMultiplier` is an additional lead time multiplier, it will affect the aiming position. You can use this to reduce the errors caused by target speed and target distance. - `Trajectory.Parabola.DetonationAngle` controls when the angle between the projectile in the current velocity direction and the horizontal plane is less than this value, it will detonate prematurely. Taking effect when the value is at -90.0 ~ 90.0 (Cannot use boundary values). - `Trajectory.Parabola.DetonationHeight` controls when the projectile is in a descending state and below the height of the launch position plus this value, it will detonate prematurely. Taking effect when it is set to non negative value. - `Trajectory.Parabola.BounceTimes` controls how many times can it bounce back when the projectile hits the ground or cliff. Be aware that excessive projectile speed may cause abnormal operation. And `Trajectory.Parabola.DetonationDistance` do not conflict with this and will take effect simultaneously. So if you want to explode the bullet only after the times of bounces is exhausted, you should set `Trajectory.Parabola.DetonationDistance` to a non positive value. @@ -746,8 +744,6 @@ Trajectory.Parabola.OpenFireMode=Speed ; ParabolaFireMode value enumera Trajectory.Parabola.ThrowHeight=600 ; integer Trajectory.Parabola.LaunchAngle=30 ; floating point value Trajectory.Parabola.LeadTimeCalculate=no ; boolean -Trajectory.Parabola.LeadTimeSimplify=no ; boolean -Trajectory.Parabola.LeadTimeMultiplier=1.0 ; floating point value Trajectory.Parabola.DetonationAngle=-90.0 ; floating point value Trajectory.Parabola.DetonationHeight=-1 ; integer Trajectory.Parabola.BounceTimes=0 ; integer diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp index bfa3562e6a..8b1cd0ff2e 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp @@ -20,8 +20,6 @@ void ParabolaTrajectoryType::Serialize(T& Stm) .Process(this->ThrowHeight) .Process(this->LaunchAngle) .Process(this->LeadTimeCalculate) - .Process(this->LeadTimeSimplify) - .Process(this->LeadTimeMultiplier) .Process(this->DetonationAngle) .Process(this->DetonationHeight) .Process(this->BounceTimes) @@ -76,8 +74,6 @@ void ParabolaTrajectoryType::Read(CCINIClass* const pINI, const char* pSection) this->ThrowHeight.Read(exINI, pSection, "Trajectory.Parabola.ThrowHeight"); this->LaunchAngle.Read(exINI, pSection, "Trajectory.Parabola.LaunchAngle"); this->LeadTimeCalculate.Read(exINI, pSection, "Trajectory.Parabola.LeadTimeCalculate"); - this->LeadTimeSimplify.Read(exINI, pSection, "Trajectory.Parabola.LeadTimeSimplify"); - this->LeadTimeMultiplier.Read(exINI, pSection, "Trajectory.Parabola.LeadTimeMultiplier"); this->DetonationAngle.Read(exINI, pSection, "Trajectory.Parabola.DetonationAngle"); this->DetonationHeight.Read(exINI, pSection, "Trajectory.Parabola.DetonationHeight"); this->BounceTimes.Read(exINI, pSection, "Trajectory.Parabola.BounceTimes"); @@ -156,12 +152,12 @@ void ParabolaTrajectory::OnUnlimbo(BulletClass* pBullet, CoordStruct* pCoord, Bu if (!pType->LeadTimeCalculate || !pTarget || resetTarget) this->PrepareForOpenFire(pBullet); else - this->WaitOneFrame.Start(1); + this->WaitOneFrame = 2; } bool ParabolaTrajectory::OnAI(BulletClass* pBullet) { - if (this->WaitOneFrame.IsTicking() && this->BulletPrepareCheck(pBullet)) + if (this->WaitOneFrame && this->BulletPrepareCheck(pBullet)) return false; if (this->BulletDetonatePreCheck(pBullet)) @@ -305,11 +301,22 @@ void ParabolaTrajectory::PrepareForOpenFire(BulletClass* pBullet) bool ParabolaTrajectory::BulletPrepareCheck(BulletClass* pBullet) { - if (this->WaitOneFrame.HasTimeLeft()) - return true; + // The time between bullets' Unlimbo() and Update() is completely uncertain. + // Technos will update its location after firing, which may result in inaccurate + // target position recorded by the LastTargetCoord in Unlimbo(). Therefore, it's + // necessary to record the position during the first Update(). - CrimRecya + if (this->WaitOneFrame == 2) + { + if (const AbstractClass* const pTarget = pBullet->Target) + { + this->LastTargetCoord = pTarget->GetCoords(); + this->WaitOneFrame = 1; + return true; + } + } + this->WaitOneFrame = 0; this->PrepareForOpenFire(pBullet); - this->WaitOneFrame.Stop(); return false; } @@ -317,66 +324,9 @@ bool ParabolaTrajectory::BulletPrepareCheck(BulletClass* pBullet) void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, CoordStruct* pSourceCoords, double gravity) { const ParabolaTrajectoryType* const pType = this->Type; - - if (pType->LeadTimeSimplify) // Only simple guess, not exact solution - { - int leadTime = 0; - - // Step 1: Guess the time of encounter between the projectile and the target based on known conditions - // Directly assume that the distance between the position where the projectile hits the target and the starting point is 4 grids - switch (pType->OpenFireMode) - { - case ParabolaFireMode::Height: - case ParabolaFireMode::HeightAndAngle: - { - // Assuming equal height - leadTime = static_cast(sqrt((this->ThrowHeight << 1) / gravity) * 1.25); - break; - } - case ParabolaFireMode::Angle: - { - double radian = pType->LaunchAngle * Math::Pi / 180.0; - radian = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? (Math::HalfPi / 3) : radian; - const double factor = Math::cos(radian); - - // Check if the angle is appropriate - if (abs(factor) < 1e-10) - break; - - const double mult = Math::sin(2 * radian); - - // Check if the angle is appropriate again - if (abs(mult) < 1e-10) - break; - - const double velocity = sqrt((Unsorted::LeptonsPerCell << 2) * gravity / mult); - - // Assuming equal height - leadTime = static_cast((Unsorted::LeptonsPerCell << 2) / (velocity * factor)); - break; - } - default: - { - // Assuming equal height - leadTime = static_cast((Unsorted::LeptonsPerCell << 2) / pType->Trajectory_Speed); - break; - } - } - - // Step 2: Substitute the time into the calculation of the attack coordinates - pBullet->TargetCoords += (pBullet->Target->GetCoords() - this->LastTargetCoord) * (pType->LeadTimeMultiplier * leadTime); - - // Step 3: Calculate the parabolic starting point vector - this->CalculateBulletVelocityRightNow(pBullet, pSourceCoords, gravity); - return; - } - CoordStruct targetCoords = pBullet->Target->GetCoords(); CoordStruct offsetCoords = pBullet->TargetCoords - targetCoords; - // A coefficient that should not exist here normally, but even so, there are still errors - const double speedFixMult = pType->LeadTimeMultiplier * 0.75; - switch (pType->OpenFireMode) { case ParabolaFireMode::Height: // Fixed max height and aim at the target @@ -385,7 +335,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C const double meetTime = this->SearchFixedHeightMeetTime(pSourceCoords, &targetCoords, &offsetCoords, gravity); // Step 2: Substitute the time into the calculation of the attack coordinates - pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); + pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * meetTime; const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; // Step 3: Check if it is an unsolvable solution @@ -417,7 +367,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C const double meetTime = this->SearchFixedAngleMeetTime(pSourceCoords, &targetCoords, &offsetCoords, radian, gravity); // Step 3: Substitute the time into the calculation of the attack coordinates - pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); + pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * meetTime; const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; // Step 4: Check if it is an unsolvable solution @@ -445,7 +395,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, pType->Trajectory_Speed); // Step 2: Substitute the time into the calculation of the attack coordinates - pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); + pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * meetTime; const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; // Step 3: Check if it is an unsolvable solution @@ -477,7 +427,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C const double meetTime = this->SearchFixedHeightMeetTime(pSourceCoords, &targetCoords, &offsetCoords, gravity); // Step 2: Substitute the time into the calculation of the attack coordinates - pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); + pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * meetTime; const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; // Step 3: Check if it is an unsolvable solution @@ -513,7 +463,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, pType->Trajectory_Speed); // Step 2: Substitute the time into the calculation of the attack coordinates - pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); + pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * meetTime; const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; // Step 3: Check if it is an unsolvable solution @@ -548,7 +498,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, pType->Trajectory_Speed); // Step 2: Substitute the time into the calculation of the attack coordinates - pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * (speedFixMult * meetTime); + pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * meetTime; const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; // Step 3: Check if it is an unsolvable solution diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h index a8b7c39824..fa77157981 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.h @@ -22,8 +22,6 @@ class ParabolaTrajectoryType final : public PhobosTrajectoryType , ThrowHeight { 600 } , LaunchAngle { 30.0 } , LeadTimeCalculate { false } - , LeadTimeSimplify { false } - , LeadTimeMultiplier { 1.0 } , DetonationAngle { -90.0 } , DetonationHeight { -1 } , BounceTimes { 0 } @@ -50,8 +48,6 @@ class ParabolaTrajectoryType final : public PhobosTrajectoryType Valueable ThrowHeight; Valueable LaunchAngle; Valueable LeadTimeCalculate; - Valueable LeadTimeSimplify; - Valueable LeadTimeMultiplier; Valueable DetonationAngle; Valueable DetonationHeight; Valueable BounceTimes; @@ -86,7 +82,7 @@ class ParabolaTrajectory final : public PhobosTrajectory , LastTargetCoord {} , CurrentBurst { 0 } , CountOfBurst { 0 } - , WaitOneFrame {} + , WaitOneFrame { 0 } , LastVelocity {} { } @@ -111,7 +107,7 @@ class ParabolaTrajectory final : public PhobosTrajectory CoordStruct LastTargetCoord; int CurrentBurst; int CountOfBurst; - CDTimerClass WaitOneFrame; + int WaitOneFrame; BulletVelocity LastVelocity; private: From 7bcb563debad97b1832dbd6da1b4cac2b8196fd9 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Fri, 8 Nov 2024 00:50:59 +0800 Subject: [PATCH 24/32] Const auto --- .../Trajectories/ParabolaTrajectory.cpp | 300 +++++++++--------- 1 file changed, 153 insertions(+), 147 deletions(-) diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp index 8b1cd0ff2e..f9a21d6afa 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp @@ -122,15 +122,15 @@ bool ParabolaTrajectory::Save(PhobosStreamWriter& Stm) const void ParabolaTrajectory::OnUnlimbo(BulletClass* pBullet, CoordStruct* pCoord, BulletVelocity* pVelocity) { - const ParabolaTrajectoryType* const pType = this->Type; + const auto pType = this->Type; this->LastTargetCoord = pBullet->TargetCoords; pBullet->Velocity = BulletVelocity::Empty; - FootClass* const pTarget = abstract_cast(pBullet->Target); + const auto pTarget = abstract_cast(pBullet->Target); bool resetTarget = false; if (pType->DetonationDistance.Get() <= -1e-10 && pTarget) { - if (CellClass* const pCell = MapClass::Instance->TryGetCellAt(pTarget->GetCoords())) + if (const auto pCell = MapClass::Instance->TryGetCellAt(pTarget->GetCoords())) { pBullet->Target = pCell; pBullet->TargetCoords = pCell->GetCoords(); @@ -138,10 +138,10 @@ void ParabolaTrajectory::OnUnlimbo(BulletClass* pBullet, CoordStruct* pCoord, Bu } } - if (WeaponTypeClass* const pWeapon = pBullet->WeaponType) + if (const auto pWeapon = pBullet->WeaponType) this->CountOfBurst = pWeapon->Burst; - if (TechnoClass* const pOwner = pBullet->Owner) + if (const auto pOwner = pBullet->Owner) { this->CurrentBurst = pOwner->CurrentBurstIndex; @@ -163,8 +163,8 @@ bool ParabolaTrajectory::OnAI(BulletClass* pBullet) if (this->BulletDetonatePreCheck(pBullet)) return true; - CellClass* const pCell = MapClass::Instance->TryGetCellAt(pBullet->Location); - const bool bounce = this->ShouldBounce; + const auto pCell = MapClass::Instance->TryGetCellAt(pBullet->Location); + const auto bounce = this->ShouldBounce; if (!pCell || (bounce && this->CalculateBulletVelocityAfterBounce(pBullet, pCell))) return true; @@ -174,23 +174,23 @@ bool ParabolaTrajectory::OnAI(BulletClass* pBullet) void ParabolaTrajectory::OnAIPreDetonate(BulletClass* pBullet) { - const Leptons targetSnapDistance = this->Type->TargetSnapDistance; + const auto targetSnapDistance = this->Type->TargetSnapDistance.Get(); if (targetSnapDistance > 0) { - const ObjectClass* const pTarget = abstract_cast(pBullet->Target); - const CoordStruct coords = pTarget ? pTarget->GetCoords() : pBullet->Data.Location; + const auto pTarget = abstract_cast(pBullet->Target); + const auto coords = pTarget ? pTarget->GetCoords() : pBullet->Data.Location; if (coords.DistanceFrom(pBullet->Location) <= targetSnapDistance) { - auto const pExt = BulletExt::ExtMap.Find(pBullet); + const auto pExt = BulletExt::ExtMap.Find(pBullet); pExt->SnappedToTarget = true; pBullet->SetLocation(coords); return; } } - const int cellHeight = MapClass::Instance->GetCellFloorHeight(pBullet->Location); + const auto cellHeight = MapClass::Instance->GetCellFloorHeight(pBullet->Location); if (pBullet->Location.Z < cellHeight) pBullet->SetLocation(CoordStruct{ pBullet->Location.X, pBullet->Location.Y, cellHeight }); @@ -213,17 +213,17 @@ TrajectoryCheckReturnType ParabolaTrajectory::OnAITechnoCheck(BulletClass* pBull void ParabolaTrajectory::PrepareForOpenFire(BulletClass* pBullet) { - const ParabolaTrajectoryType* const pType = this->Type; - const AbstractClass* const pTarget = pBullet->Target; + const auto pType = this->Type; + const auto pTarget = pBullet->Target; bool leadTimeCalculate = pType->LeadTimeCalculate && pTarget; - CoordStruct theTargetCoords = leadTimeCalculate ? pTarget->GetCoords() : pBullet->TargetCoords; - CoordStruct theSourceCoords = leadTimeCalculate ? pBullet->Location : pBullet->SourceCoords; + auto theTargetCoords = leadTimeCalculate ? pTarget->GetCoords() : pBullet->TargetCoords; + auto theSourceCoords = leadTimeCalculate ? pBullet->Location : pBullet->SourceCoords; leadTimeCalculate &= theTargetCoords != this->LastTargetCoord; double rotateAngle = 0.0; if (!pType->LeadTimeCalculate && theTargetCoords == theSourceCoords && pBullet->Owner) //For disperse. { - const CoordStruct theOwnerCoords = pBullet->Owner->GetCoords(); + const auto theOwnerCoords = pBullet->Owner->GetCoords(); rotateAngle = Math::atan2(theTargetCoords.Y - theOwnerCoords.Y , theTargetCoords.X - theOwnerCoords.X); } else @@ -240,16 +240,16 @@ void ParabolaTrajectory::PrepareForOpenFire(BulletClass* pBullet) if (pBullet->Type->Inaccurate) { - auto const pTypeExt = BulletTypeExt::ExtMap.Find(pBullet->Type); - const double offsetMult = 0.0004 * theSourceCoords.DistanceFrom(theTargetCoords); - const int offsetMin = static_cast(offsetMult * pTypeExt->BallisticScatter_Min.Get(Leptons(0))); - const int offsetMax = static_cast(offsetMult * pTypeExt->BallisticScatter_Max.Get(Leptons(RulesClass::Instance->BallisticScatter))); - const int offsetDistance = ScenarioClass::Instance->Random.RandomRanged(offsetMin, offsetMax); + const auto pTypeExt = BulletTypeExt::ExtMap.Find(pBullet->Type); + const auto offsetMult = 0.0004 * theSourceCoords.DistanceFrom(theTargetCoords); + const auto offsetMin = static_cast(offsetMult * pTypeExt->BallisticScatter_Min.Get(Leptons(0))); + const auto offsetMax = static_cast(offsetMult * pTypeExt->BallisticScatter_Max.Get(Leptons(RulesClass::Instance->BallisticScatter))); + const auto offsetDistance = ScenarioClass::Instance->Random.RandomRanged(offsetMin, offsetMax); theTargetCoords = MapClass::GetRandomCoordsNear(theTargetCoords, offsetDistance, false); } pBullet->TargetCoords = theTargetCoords; - const double gravity = BulletTypeExt::GetAdjustedGravity(pBullet->Type); + const auto gravity = BulletTypeExt::GetAdjustedGravity(pBullet->Type); if (gravity <= 1e-10) { @@ -265,7 +265,7 @@ void ParabolaTrajectory::PrepareForOpenFire(BulletClass* pBullet) if (!this->UseDisperseBurst && abs(pType->RotateCoord) > 1e-10 && this->CountOfBurst > 1) { - const CoordStruct axis = pType->AxisOfRotation; + const auto axis = pType->AxisOfRotation.Get(); BulletVelocity rotationAxis { @@ -274,7 +274,7 @@ void ParabolaTrajectory::PrepareForOpenFire(BulletClass* pBullet) static_cast(axis.Z) }; - const double rotationAxisLengthSquared = rotationAxis.MagnitudeSquared(); + const auto rotationAxisLengthSquared = rotationAxis.MagnitudeSquared(); if (abs(rotationAxisLengthSquared) > 1e-10) { @@ -293,7 +293,7 @@ void ParabolaTrajectory::PrepareForOpenFire(BulletClass* pBullet) extraRotate = Math::Pi * (pType->RotateCoord * (this->CurrentBurst / (this->CountOfBurst - 1.0) - 0.5)) / 180; } - const double cosRotate = Math::cos(extraRotate); + const auto cosRotate = Math::cos(extraRotate); pBullet->Velocity = (pBullet->Velocity * cosRotate) + (rotationAxis * ((1 - cosRotate) * (pBullet->Velocity * rotationAxis))) + (rotationAxis.CrossProduct(pBullet->Velocity) * Math::sin(extraRotate)); } } @@ -307,7 +307,7 @@ bool ParabolaTrajectory::BulletPrepareCheck(BulletClass* pBullet) // necessary to record the position during the first Update(). - CrimRecya if (this->WaitOneFrame == 2) { - if (const AbstractClass* const pTarget = pBullet->Target) + if (const auto pTarget = pBullet->Target) { this->LastTargetCoord = pTarget->GetCoords(); this->WaitOneFrame = 1; @@ -323,20 +323,20 @@ bool ParabolaTrajectory::BulletPrepareCheck(BulletClass* pBullet) void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, CoordStruct* pSourceCoords, double gravity) { - const ParabolaTrajectoryType* const pType = this->Type; - CoordStruct targetCoords = pBullet->Target->GetCoords(); - CoordStruct offsetCoords = pBullet->TargetCoords - targetCoords; + const auto pType = this->Type; + auto targetCoords = pBullet->Target->GetCoords(); + auto offsetCoords = pBullet->TargetCoords - targetCoords; switch (pType->OpenFireMode) { case ParabolaFireMode::Height: // Fixed max height and aim at the target { // Step 1: Using Newton Iteration Method to determine the time of encounter between the projectile and the target - const double meetTime = this->SearchFixedHeightMeetTime(pSourceCoords, &targetCoords, &offsetCoords, gravity); + const auto meetTime = this->SearchFixedHeightMeetTime(pSourceCoords, &targetCoords, &offsetCoords, gravity); // Step 2: Substitute the time into the calculation of the attack coordinates pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * meetTime; - const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; + const auto destinationCoords = pBullet->TargetCoords - *pSourceCoords; // Step 3: Check if it is an unsolvable solution if (meetTime <= 1e-10 || destinationCoords.Magnitude() <= 1e-10) @@ -347,8 +347,9 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C pBullet->Velocity.Y = destinationCoords.Y / meetTime; // Step 5: Determine the maximum height that the projectile should reach - const int sourceHeight = pSourceCoords->Z, targetHeight = sourceHeight + destinationCoords.Z; - const int maxHeight = destinationCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; + const auto sourceHeight = pSourceCoords->Z; + const auto targetHeight = sourceHeight + destinationCoords.Z; + const auto maxHeight = destinationCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; // Step 6: Calculate the vertical component of the projectile velocity pBullet->Velocity.Z = sqrt(2 * gravity * (maxHeight - sourceHeight)) + gravity / 2; @@ -360,15 +361,15 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C case ParabolaFireMode::Angle: // Fixed fire angle and aim at the target { // Step 1: Read the appropriate fire angle - double radian = pType->LaunchAngle * Math::Pi / 180.0; + auto radian = pType->LaunchAngle * Math::Pi / 180.0; radian = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? (Math::HalfPi / 3) : radian; // Step 2: Using Newton Iteration Method to determine the time of encounter between the projectile and the target - const double meetTime = this->SearchFixedAngleMeetTime(pSourceCoords, &targetCoords, &offsetCoords, radian, gravity); + const auto meetTime = this->SearchFixedAngleMeetTime(pSourceCoords, &targetCoords, &offsetCoords, radian, gravity); // Step 3: Substitute the time into the calculation of the attack coordinates pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * meetTime; - const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; + const auto destinationCoords = pBullet->TargetCoords - *pSourceCoords; // Step 4: Check if it is an unsolvable solution if (meetTime <= 1e-10 || destinationCoords.Magnitude() <= 1e-10) @@ -379,8 +380,8 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C pBullet->Velocity.Y = destinationCoords.Y / meetTime; // Step 6: Calculate whole horizontal component of the projectile velocity - const double horizontalDistance = Point2D{ destinationCoords.X, destinationCoords.Y }.Magnitude(); - const double horizontalVelocity = horizontalDistance / meetTime; + const auto horizontalDistance = Point2D { destinationCoords.X, destinationCoords.Y }.Magnitude(); + const auto horizontalVelocity = horizontalDistance / meetTime; // Step 7: Calculate the vertical component of the projectile velocity pBullet->Velocity.Z = horizontalVelocity * Math::tan(radian) + gravity / 2; @@ -392,7 +393,7 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C case ParabolaFireMode::SpeedAndHeight: // Fixed horizontal speed and fixed max height { // Step 1: Calculate the time when the projectile meets the target directly using horizontal velocity - const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, pType->Trajectory_Speed); + const auto meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, pType->Trajectory_Speed); // Step 2: Substitute the time into the calculation of the attack coordinates pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * meetTime; @@ -403,16 +404,17 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C break; // Step 4: Calculate the ratio of horizontal velocity to horizontal distance - const double horizontalDistance = Point2D{ destinationCoords.X, destinationCoords.Y }.Magnitude(); - const double mult = horizontalDistance > 1e-10 ? pType->Trajectory_Speed / horizontalDistance : 1.0; + const auto horizontalDistance = Point2D { destinationCoords.X, destinationCoords.Y }.Magnitude(); + const auto mult = horizontalDistance > 1e-10 ? pType->Trajectory_Speed / horizontalDistance : 1.0; // Step 5: Calculate the horizontal component of the projectile velocity pBullet->Velocity.X = destinationCoords.X * mult; pBullet->Velocity.Y = destinationCoords.Y * mult; // Step 6: Determine the maximum height that the projectile should reach - const int sourceHeight = pSourceCoords->Z, targetHeight = sourceHeight + destinationCoords.Z; - const int maxHeight = destinationCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; + const auto sourceHeight = pSourceCoords->Z; + const auto targetHeight = sourceHeight + destinationCoords.Z; + const auto maxHeight = destinationCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; // Step 7: Calculate the vertical component of the projectile velocity pBullet->Velocity.Z = sqrt(2 * gravity * (maxHeight - sourceHeight)) + gravity / 2; @@ -424,30 +426,31 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C case ParabolaFireMode::HeightAndAngle: // Fixed max height and fixed fire angle { // Step 1: Using Newton Iteration Method to determine the time of encounter between the projectile and the target - const double meetTime = this->SearchFixedHeightMeetTime(pSourceCoords, &targetCoords, &offsetCoords, gravity); + const auto meetTime = this->SearchFixedHeightMeetTime(pSourceCoords, &targetCoords, &offsetCoords, gravity); // Step 2: Substitute the time into the calculation of the attack coordinates pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * meetTime; - const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; + const auto destinationCoords = pBullet->TargetCoords - *pSourceCoords; // Step 3: Check if it is an unsolvable solution if (meetTime <= 1e-10 || destinationCoords.Magnitude() <= 1e-10) break; // Step 4: Determine the maximum height that the projectile should reach - const int sourceHeight = pSourceCoords->Z, targetHeight = sourceHeight + destinationCoords.Z; - const int maxHeight = destinationCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; + const auto sourceHeight = pSourceCoords->Z; + const auto targetHeight = sourceHeight + destinationCoords.Z; + const auto maxHeight = destinationCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; // Step 5: Calculate the vertical component of the projectile velocity pBullet->Velocity.Z = sqrt(2 * gravity * (maxHeight - sourceHeight)) + gravity / 2; // Step 6: Read the appropriate fire angle - double radian = pType->LaunchAngle * Math::Pi / 180.0; + auto radian = pType->LaunchAngle * Math::Pi / 180.0; radian = (radian >= Math::HalfPi || radian <= 1e-10) ? (Math::HalfPi / 3) : radian; // Step 7: Calculate the ratio of horizontal velocity to horizontal distance - const double horizontalDistance = Point2D{ destinationCoords.X, destinationCoords.Y }.Magnitude(); - const double mult = (pBullet->Velocity.Z / Math::tan(radian)) / horizontalDistance; + const auto horizontalDistance = Point2D { destinationCoords.X, destinationCoords.Y }.Magnitude(); + const auto mult = (pBullet->Velocity.Z / Math::tan(radian)) / horizontalDistance; // Step 8: Calculate the horizontal component of the projectile velocity pBullet->Velocity.X = destinationCoords.X * mult; @@ -460,29 +463,29 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C case ParabolaFireMode::SpeedAndAngle: // Fixed horizontal speed and fixed fire angle { // Step 1: Calculate the time when the projectile meets the target directly using horizontal velocity - const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, pType->Trajectory_Speed); + const auto meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, pType->Trajectory_Speed); // Step 2: Substitute the time into the calculation of the attack coordinates pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * meetTime; - const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; + const auto destinationCoords = pBullet->TargetCoords - *pSourceCoords; // Step 3: Check if it is an unsolvable solution if (meetTime <= 1e-10 || destinationCoords.Magnitude() <= 1e-10) break; // Step 4: Calculate the ratio of horizontal velocity to horizontal distance - const double horizontalDistance = Point2D{ destinationCoords.X, destinationCoords.Y }.Magnitude(); - const double mult = horizontalDistance > 1e-10 ? pType->Trajectory_Speed / horizontalDistance : 1.0; + const auto horizontalDistance = Point2D { destinationCoords.X, destinationCoords.Y }.Magnitude(); + const auto mult = horizontalDistance > 1e-10 ? pType->Trajectory_Speed / horizontalDistance : 1.0; // Step 5: Calculate each horizontal component of the projectile velocity pBullet->Velocity.X = destinationCoords.X * mult; pBullet->Velocity.Y = destinationCoords.Y * mult; // Step 6: Calculate whole horizontal component of the projectile velocity - const double horizontalVelocity = horizontalDistance * mult; + const auto horizontalVelocity = horizontalDistance * mult; // Step 7: Read the appropriate fire angle - double radian = pType->LaunchAngle * Math::Pi / 180.0; + auto radian = pType->LaunchAngle * Math::Pi / 180.0; radian = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? (Math::HalfPi / 3) : radian; // Step 8: Calculate the vertical component of the projectile velocity @@ -495,19 +498,19 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C default: // Fixed horizontal speed and aim at the target { // Step 1: Calculate the time when the projectile meets the target directly using horizontal velocity - const double meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, pType->Trajectory_Speed); + const auto meetTime = this->SolveFixedSpeedMeetTime(pSourceCoords, &targetCoords, &offsetCoords, pType->Trajectory_Speed); // Step 2: Substitute the time into the calculation of the attack coordinates pBullet->TargetCoords += (targetCoords - this->LastTargetCoord) * meetTime; - const CoordStruct destinationCoords = pBullet->TargetCoords - *pSourceCoords; + const auto destinationCoords = pBullet->TargetCoords - *pSourceCoords; // Step 3: Check if it is an unsolvable solution if (meetTime <= 1e-10 || destinationCoords.Magnitude() <= 1e-10) break; // Step 4: Calculate the ratio of horizontal velocity to horizontal distance - const double horizontalDistance = Point2D{ destinationCoords.X, destinationCoords.Y }.Magnitude(); - const double mult = horizontalDistance > 1e-10 ? pType->Trajectory_Speed / horizontalDistance : 1.0; + const auto horizontalDistance = Point2D { destinationCoords.X, destinationCoords.Y }.Magnitude(); + const auto mult = horizontalDistance > 1e-10 ? pType->Trajectory_Speed / horizontalDistance : 1.0; // Step 5: Calculate the projectile velocity pBullet->Velocity.X = destinationCoords.X * mult; @@ -529,11 +532,11 @@ void ParabolaTrajectory::CalculateBulletVelocityLeadTime(BulletClass* pBullet, C void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, CoordStruct* pSourceCoords, double gravity) { - const ParabolaTrajectoryType* const pType = this->Type; + const auto pType = this->Type; // Calculate horizontal distance - const CoordStruct distanceCoords = pBullet->TargetCoords - *pSourceCoords; - const double distance = distanceCoords.Magnitude(); - const double horizontalDistance = Point2D{ distanceCoords.X, distanceCoords.Y }.Magnitude(); + const auto distanceCoords = pBullet->TargetCoords - *pSourceCoords; + const auto distance = distanceCoords.Magnitude(); + const auto horizontalDistance = Point2D { distanceCoords.X, distanceCoords.Y }.Magnitude(); if (distance <= 1e-10) { @@ -547,14 +550,15 @@ void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, C case ParabolaFireMode::Height: // Fixed max height and aim at the target { // Step 1: Determine the maximum height that the projectile should reach - const int sourceHeight = pSourceCoords->Z, targetHeight = pBullet->TargetCoords.Z; - const int maxHeight = distanceCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; + const auto sourceHeight = pSourceCoords->Z; + const auto targetHeight = pBullet->TargetCoords.Z; + const auto maxHeight = distanceCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; // Step 2: Calculate the vertical component of the projectile velocity pBullet->Velocity.Z = sqrt(2 * gravity * (maxHeight - sourceHeight)); // Step 3: Calculate the total time it takes for the projectile to meet the target using the heights of the ascending and descending phases - const double meetTime = sqrt(2 * (maxHeight - sourceHeight) / gravity) + sqrt(2 * (maxHeight - targetHeight) / gravity); + const auto meetTime = sqrt(2 * (maxHeight - sourceHeight) / gravity) + sqrt(2 * (maxHeight - targetHeight) / gravity); // Step 4: Calculate the horizontal component of the projectile velocity pBullet->Velocity.X = distanceCoords.X / meetTime; @@ -564,16 +568,16 @@ void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, C case ParabolaFireMode::Angle: // Fixed fire angle and aim at the target { // Step 1: Read the appropriate fire angle - double radian = pType->LaunchAngle * Math::Pi / 180.0; + const auto radian = pType->LaunchAngle * Math::Pi / 180.0; // Step 2: Using Newton Iteration Method to determine the projectile velocity - double velocity = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? 100.0 : this->SearchVelocity(horizontalDistance, distanceCoords.Z, radian, gravity); + const auto velocity = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? 100.0 : this->SearchVelocity(horizontalDistance, distanceCoords.Z, radian, gravity); // Step 3: Calculate the vertical component of the projectile velocity pBullet->Velocity.Z = velocity * Math::sin(radian); // Step 4: Calculate the ratio of horizontal velocity to horizontal distance - const double mult = velocity * Math::cos(radian) / horizontalDistance; + const auto mult = velocity * Math::cos(radian) / horizontalDistance; // Step 5: Calculate the horizontal component of the projectile velocity pBullet->Velocity.X = distanceCoords.X * mult; @@ -583,14 +587,15 @@ void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, C case ParabolaFireMode::SpeedAndHeight: // Fixed horizontal speed and fixed max height { // Step 1: Determine the maximum height that the projectile should reach - const int sourceHeight = pSourceCoords->Z, targetHeight = pBullet->TargetCoords.Z; - const int maxHeight = distanceCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; + const auto sourceHeight = pSourceCoords->Z; + const auto targetHeight = pBullet->TargetCoords.Z; + const auto maxHeight = distanceCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; // Step 2: Calculate the vertical component of the projectile velocity pBullet->Velocity.Z = sqrt(2 * gravity * (maxHeight - sourceHeight)); // Step 3: Calculate the ratio of horizontal velocity to horizontal distance - const double mult = horizontalDistance > 1e-10 ? pType->Trajectory_Speed / horizontalDistance : 1.0; + const auto mult = horizontalDistance > 1e-10 ? pType->Trajectory_Speed / horizontalDistance : 1.0; // Step 4: Calculate the horizontal component of the projectile velocity pBullet->Velocity.X = distanceCoords.X * mult; @@ -600,18 +605,19 @@ void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, C case ParabolaFireMode::HeightAndAngle: // Fixed max height and fixed fire angle { // Step 1: Determine the maximum height that the projectile should reach - const int sourceHeight = pSourceCoords->Z, targetHeight = pBullet->TargetCoords.Z; - const int maxHeight = distanceCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; + const auto sourceHeight = pSourceCoords->Z; + const auto targetHeight = pBullet->TargetCoords.Z; + const auto maxHeight = distanceCoords.Z > 0 ? this->ThrowHeight + targetHeight : this->ThrowHeight + sourceHeight; // Step 2: Calculate the vertical component of the projectile velocity pBullet->Velocity.Z = sqrt(2 * gravity * (maxHeight - sourceHeight)); // Step 3: Read the appropriate fire angle - double radian = pType->LaunchAngle * Math::Pi / 180.0; + auto radian = pType->LaunchAngle * Math::Pi / 180.0; radian = (radian >= Math::HalfPi || radian <= 1e-10) ? (Math::HalfPi / 3) : radian; // Step 4: Calculate the ratio of horizontal velocity to horizontal distance - const double mult = (pBullet->Velocity.Z / Math::tan(radian)) / horizontalDistance; + const auto mult = (pBullet->Velocity.Z / Math::tan(radian)) / horizontalDistance; // Step 5: Calculate the horizontal component of the projectile velocity pBullet->Velocity.X = distanceCoords.X * mult; @@ -621,14 +627,14 @@ void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, C case ParabolaFireMode::SpeedAndAngle: // Fixed horizontal speed and fixed fire angle { // Step 1: Calculate the ratio of horizontal velocity to horizontal distance - const double mult = horizontalDistance > 1e-10 ? pType->Trajectory_Speed / horizontalDistance : 1.0; + const auto mult = horizontalDistance > 1e-10 ? pType->Trajectory_Speed / horizontalDistance : 1.0; // Step 2: Calculate the horizontal component of the projectile velocity pBullet->Velocity.X = distanceCoords.X * mult; pBullet->Velocity.Y = distanceCoords.Y * mult; // Step 3: Read the appropriate fire angle - double radian = pType->LaunchAngle * Math::Pi / 180.0; + auto radian = pType->LaunchAngle * Math::Pi / 180.0; radian = (radian >= Math::HalfPi || radian <= -Math::HalfPi) ? (Math::HalfPi / 3) : radian; // Step 4: Calculate the vertical component of the projectile velocity @@ -638,7 +644,7 @@ void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, C default: // Fixed horizontal speed and aim at the target { // Step 1: Calculate the ratio of horizontal velocity to horizontal distance - const double mult = horizontalDistance > 1e-10 ? pType->Trajectory_Speed / horizontalDistance : 1.0; + const auto mult = horizontalDistance > 1e-10 ? pType->Trajectory_Speed / horizontalDistance : 1.0; // Step 2: Calculate the projectile velocity pBullet->Velocity.X = distanceCoords.X * mult; @@ -657,7 +663,7 @@ void ParabolaTrajectory::CalculateBulletVelocityRightNow(BulletClass* pBullet, C void ParabolaTrajectory::CheckIfNeedExtraCheck(BulletClass* pBullet) { - const ParabolaTrajectoryType* const pType = this->Type; + const auto pType = this->Type; switch (pType->OpenFireMode) { @@ -679,29 +685,29 @@ void ParabolaTrajectory::CheckIfNeedExtraCheck(BulletClass* pBullet) double ParabolaTrajectory::SearchVelocity(double horizontalDistance, int distanceCoordsZ, double radian, double gravity) { // Estimate initial velocity - const double mult = Math::sin(2 * radian); - double velocity = abs(mult) > 1e-10 ? sqrt(horizontalDistance * gravity / mult) : 0.0; + const auto mult = Math::sin(2 * radian); + auto velocity = abs(mult) > 1e-10 ? sqrt(horizontalDistance * gravity / mult) : 0.0; velocity += distanceCoordsZ / gravity; velocity = velocity > 8.0 ? velocity : 8.0; - const double error = velocity / 16; + const auto error = velocity / 16; // Step size - const double delta = 1e-5; + const auto delta = 1e-5; // Newton Iteration Method for (int i = 0; i < 10; ++i) { // Substitute into the estimate speed - const double differential = this->CheckVelocityEquation(horizontalDistance, distanceCoordsZ, velocity, radian, gravity); - const double dDifferential = (this->CheckVelocityEquation(horizontalDistance, distanceCoordsZ, (velocity + delta), radian, gravity) - differential) / delta; + const auto differential = this->CheckVelocityEquation(horizontalDistance, distanceCoordsZ, velocity, radian, gravity); + const auto dDifferential = (this->CheckVelocityEquation(horizontalDistance, distanceCoordsZ, (velocity + delta), radian, gravity) - differential) / delta; // Check unacceptable divisor if (abs(dDifferential) < 1e-10) return velocity; // Calculate the speed of the next iteration - const double difference = differential / dDifferential; - const double velocityNew = velocity - difference; + const auto difference = differential / dDifferential; + const auto velocityNew = velocity - difference; // Check tolerable error if (abs(difference) < error) @@ -718,20 +724,20 @@ double ParabolaTrajectory::SearchVelocity(double horizontalDistance, int distanc double ParabolaTrajectory::CheckVelocityEquation(double horizontalDistance, int distanceCoordsZ, double velocity, double radian, double gravity) { // Calculate each component of the projectile velocity - const double horizontalVelocity = velocity * Math::cos(radian); - const double verticalVelocity = velocity * Math::sin(radian); + const auto horizontalVelocity = velocity * Math::cos(radian); + const auto verticalVelocity = velocity * Math::sin(radian); // Calculate the time of the rising phase - const double upTime = verticalVelocity / gravity; + const auto upTime = verticalVelocity / gravity; // Calculate the maximum height that the projectile can reach - const double maxHeight = 0.5 * verticalVelocity * upTime; + const auto maxHeight = 0.5 * verticalVelocity * upTime; // Calculate the time of the descent phase - const double downTime = sqrt(2 * (maxHeight - distanceCoordsZ) / gravity); + const auto downTime = sqrt(2 * (maxHeight - distanceCoordsZ) / gravity); // Calculate the total time required for horizontal movement - const double wholeTime = horizontalDistance / horizontalVelocity; + const auto wholeTime = horizontalDistance / horizontalVelocity; // Calculate the difference between the total vertical motion time and the total horizontal motion time return wholeTime - (upTime + downTime); @@ -747,14 +753,14 @@ double ParabolaTrajectory::SolveFixedSpeedMeetTime(CoordStruct* pSourceCrd, Coor // (destinationCrd + targetSpeedCrd * time).Magnitude() = horizontalSpeed * time // Solve this quadratic equation - const double divisor = (targetSpeedCrd.MagnitudeSquared() - horizontalSpeed * horizontalSpeed) * 2; - const double factor = 2 * (targetSpeedCrd * destinationCrd); - const double delta = factor * factor - 2 * divisor * destinationCrd.MagnitudeSquared(); + const auto divisor = (targetSpeedCrd.MagnitudeSquared() - horizontalSpeed * horizontalSpeed) * 2; + const auto factor = 2 * (targetSpeedCrd * destinationCrd); + const auto delta = factor * factor - 2 * divisor * destinationCrd.MagnitudeSquared(); if (delta >= 1e-10) { - const double timeP = (-factor + sqrt(delta)) / divisor; - const double timeM = (-factor - sqrt(delta)) / divisor; + const auto timeP = (-factor + sqrt(delta)) / divisor; + const auto timeM = (-factor - sqrt(delta)) / divisor; if (timeM > 1e-10 && timeP > 1e-10) return timeM < timeP ? timeM : timeP; @@ -770,19 +776,19 @@ double ParabolaTrajectory::SolveFixedSpeedMeetTime(CoordStruct* pSourceCrd, Coor double ParabolaTrajectory::SearchFixedHeightMeetTime(CoordStruct* pSourceCrd, CoordStruct* pTargetCrd, CoordStruct* pOffsetCrd, double gravity) { // Similar to method SearchVelocity, no further elaboration will be provided - const double delta = 1e-5; - double meetTime = (this->ThrowHeight << 2) / gravity; + const auto delta = 1e-5; + auto meetTime = (this->ThrowHeight << 2) / gravity; for (int i = 0; i < 10; ++i) { - const double differential = this->CheckFixedHeightEquation(pSourceCrd, pTargetCrd, pOffsetCrd, meetTime, gravity); - const double dDifferential = (this->CheckFixedHeightEquation(pSourceCrd, pTargetCrd, pOffsetCrd, (meetTime + delta), gravity) - differential) / delta; + const auto differential = this->CheckFixedHeightEquation(pSourceCrd, pTargetCrd, pOffsetCrd, meetTime, gravity); + const auto dDifferential = (this->CheckFixedHeightEquation(pSourceCrd, pTargetCrd, pOffsetCrd, (meetTime + delta), gravity) - differential) / delta; if (abs(dDifferential) < 1e-10) return meetTime; - const double difference = differential / dDifferential; - const double meetTimeNew = meetTime - difference; + const auto difference = differential / dDifferential; + const auto meetTimeNew = meetTime - difference; if (abs(difference) < 1.0) return meetTimeNew; @@ -796,10 +802,10 @@ double ParabolaTrajectory::SearchFixedHeightMeetTime(CoordStruct* pSourceCrd, Co double ParabolaTrajectory::CheckFixedHeightEquation(CoordStruct* pSourceCrd, CoordStruct* pTargetCrd, CoordStruct* pOffsetCrd, double meetTime, double gravity) { // Calculate how high the target will reach during this period of time - const int meetHeight = static_cast((pTargetCrd->Z - this->LastTargetCoord.Z) * meetTime) + pTargetCrd->Z + pOffsetCrd->Z; + const auto meetHeight = static_cast((pTargetCrd->Z - this->LastTargetCoord.Z) * meetTime) + pTargetCrd->Z + pOffsetCrd->Z; // Calculate how high the projectile can fly during this period of time - const int maxHeight = meetHeight > pSourceCrd->Z ? this->ThrowHeight + meetHeight : this->ThrowHeight + pSourceCrd->Z; + const auto maxHeight = meetHeight > pSourceCrd->Z ? this->ThrowHeight + meetHeight : this->ThrowHeight + pSourceCrd->Z; // Calculate the difference between these two times return sqrt((maxHeight - pSourceCrd->Z) * 2 / gravity) + sqrt((maxHeight - meetHeight) * 2 / gravity) - meetTime; @@ -808,19 +814,19 @@ double ParabolaTrajectory::CheckFixedHeightEquation(CoordStruct* pSourceCrd, Coo double ParabolaTrajectory::SearchFixedAngleMeetTime(CoordStruct* pSourceCrd, CoordStruct* pTargetCrd, CoordStruct* pOffsetCrd, double radian, double gravity) { // Similar to method SearchVelocity, no further elaboration will be provided - const double delta = 1e-5; - double meetTime = 512 * Math::sin(radian) / gravity; + const auto delta = 1e-5; + auto meetTime = 512 * Math::sin(radian) / gravity; for (int i = 0; i < 10; ++i) { - const double differential = this->CheckFixedAngleEquation(pSourceCrd, pTargetCrd, pOffsetCrd, meetTime, radian, gravity); - const double dDifferential = (this->CheckFixedAngleEquation(pSourceCrd, pTargetCrd, pOffsetCrd, (meetTime + delta), radian, gravity) - differential) / delta; + const auto differential = this->CheckFixedAngleEquation(pSourceCrd, pTargetCrd, pOffsetCrd, meetTime, radian, gravity); + const auto dDifferential = (this->CheckFixedAngleEquation(pSourceCrd, pTargetCrd, pOffsetCrd, (meetTime + delta), radian, gravity) - differential) / delta; if (abs(dDifferential) < 1e-10) return meetTime; - const double difference = differential / dDifferential; - const double meetTimeNew = meetTime - difference; + const auto difference = differential / dDifferential; + const auto meetTimeNew = meetTime - difference; if (abs(difference) < 1.0) return meetTimeNew; @@ -834,25 +840,25 @@ double ParabolaTrajectory::SearchFixedAngleMeetTime(CoordStruct* pSourceCrd, Coo double ParabolaTrajectory::CheckFixedAngleEquation(CoordStruct* pSourceCrd, CoordStruct* pTargetCrd, CoordStruct* pOffsetCrd, double meetTime, double radian, double gravity) { // Using the estimated time to obtain the predicted location of the target - const CoordStruct distanceCoords = (*pTargetCrd - this->LastTargetCoord) * meetTime + *pTargetCrd + *pOffsetCrd - *pSourceCrd; + const auto distanceCoords = (*pTargetCrd - this->LastTargetCoord) * meetTime + *pTargetCrd + *pOffsetCrd - *pSourceCrd; // Calculate the horizontal distance between the target and the calculation - const double horizontalDistance = Point2D{ distanceCoords.X, distanceCoords.Y }.Magnitude(); + const auto horizontalDistance = Point2D{ distanceCoords.X, distanceCoords.Y }.Magnitude(); // Calculate the horizontal velocity - const double horizontalVelocity = horizontalDistance / meetTime; + const auto horizontalVelocity = horizontalDistance / meetTime; // Calculate the vertical velocity - const double verticalVelocity = horizontalVelocity * Math::tan(radian); + const auto verticalVelocity = horizontalVelocity * Math::tan(radian); // Calculate the time of the rising phase - const double upTime = verticalVelocity / gravity; + const auto upTime = verticalVelocity / gravity; // Calculate the maximum height that the projectile can reach - const double maxHeight = 0.5 * verticalVelocity * upTime; + const auto maxHeight = 0.5 * verticalVelocity * upTime; // Calculate the time of the descent phase - const double downTime = sqrt(2 * (maxHeight - distanceCoords.Z) / gravity); + const auto downTime = sqrt(2 * (maxHeight - distanceCoords.Z) / gravity); // Calculate the difference between the actual flight time of the projectile obtained and the initially estimated time return upTime + downTime - meetTime; @@ -860,7 +866,7 @@ double ParabolaTrajectory::CheckFixedAngleEquation(CoordStruct* pSourceCrd, Coor bool ParabolaTrajectory::CalculateBulletVelocityAfterBounce(BulletClass* pBullet, CellClass* pCell) { - const ParabolaTrajectoryType* const pType = this->Type; + const auto pType = this->Type; if (pCell->LandType == LandType::Water && !pType->BounceOnWater) return true; @@ -868,13 +874,13 @@ bool ParabolaTrajectory::CalculateBulletVelocityAfterBounce(BulletClass* pBullet --this->BounceTimes; this->ShouldBounce = false; - const BulletVelocity groundNormalVector = this->GetGroundNormalVector(pBullet, pCell); + const auto groundNormalVector = this->GetGroundNormalVector(pBullet, pCell); pBullet->Velocity = (this->LastVelocity - groundNormalVector * (this->LastVelocity * groundNormalVector) * 2) * pType->BounceCoefficient; if (pType->BounceDetonate) { - TechnoClass* const pFirer = pBullet->Owner; - HouseClass* const pOwner = pFirer ? pFirer->Owner : BulletExt::ExtMap.Find(pBullet)->FirerHouse; + const auto pFirer = pBullet->Owner; + const auto pOwner = pFirer ? pFirer->Owner : BulletExt::ExtMap.Find(pBullet)->FirerHouse; WarheadTypeExt::DetonateAt(pBullet->WH, pBullet->Location, pFirer, pBullet->Health, pOwner); } @@ -891,7 +897,7 @@ bool ParabolaTrajectory::CalculateBulletVelocityAfterBounce(BulletClass* pBullet BulletVelocity ParabolaTrajectory::GetGroundNormalVector(BulletClass* pBullet, CellClass* pCell) { - if (const unsigned char index = pCell->SlopeIndex) + if (const auto index = pCell->SlopeIndex) { Vector2D factor { 0.0, 0.0 }; @@ -940,19 +946,19 @@ BulletVelocity ParabolaTrajectory::GetGroundNormalVector(BulletClass* pBullet, C } // 362.1 -> Unsorted::LeptonsPerCell * sqrt(2) - const double horizontalVelocity = Vector2D{ pBullet->Velocity.X, pBullet->Velocity.Y }.Magnitude(); - const BulletVelocity velocity = horizontalVelocity > 362.1 ? pBullet->Velocity * (362.1 / horizontalVelocity) : pBullet->Velocity; + const auto horizontalVelocity = Vector2D{ pBullet->Velocity.X, pBullet->Velocity.Y }.Magnitude(); + const auto velocity = horizontalVelocity > 362.1 ? pBullet->Velocity * (362.1 / horizontalVelocity) : pBullet->Velocity; const CoordStruct velocityCoords { static_cast(velocity.X), static_cast(velocity.Y), static_cast(velocity.Z) }; - const int cellHeight = pCell->Level * Unsorted::LevelHeight; - const int bulletHeight = pBullet->Location.Z; - const int lastCellHeight = MapClass::Instance->GetCellFloorHeight(pBullet->Location - velocityCoords); + const auto cellHeight = pCell->Level * Unsorted::LevelHeight; + const auto bulletHeight = pBullet->Location.Z; + const auto lastCellHeight = MapClass::Instance->GetCellFloorHeight(pBullet->Location - velocityCoords); if (bulletHeight < cellHeight && (cellHeight - lastCellHeight) > 384) { - CellStruct cell = pCell->MapCoords; - const short reverseSgnX = pBullet->Velocity.X > 0.0 ? -1 : 1; - const short reverseSgnY = pBullet->Velocity.Y > 0.0 ? -1 : 1; + auto cell = pCell->MapCoords; + const auto reverseSgnX = static_cast(pBullet->Velocity.X > 0.0 ? -1 : 1); + const auto reverseSgnY = static_cast(pBullet->Velocity.Y > 0.0 ? -1 : 1); int index = 0; if (this->CheckBulletHitCliff(cell.X + reverseSgnX, cell.Y, bulletHeight, lastCellHeight)) @@ -999,9 +1005,9 @@ BulletVelocity ParabolaTrajectory::GetGroundNormalVector(BulletClass* pBullet, C bool ParabolaTrajectory::CheckBulletHitCliff(short X, short Y, int bulletHeight, int lastCellHeight) { - if (CellClass* const pCell = MapClass::Instance->TryGetCellAt(CellStruct{ X, Y })) + if (const auto pCell = MapClass::Instance->TryGetCellAt(CellStruct{ X, Y })) { - const int cellHeight = pCell->Level * Unsorted::LevelHeight; + const auto cellHeight = pCell->Level * Unsorted::LevelHeight; if (bulletHeight < cellHeight && (cellHeight - lastCellHeight) > 384) return true; @@ -1015,7 +1021,7 @@ bool ParabolaTrajectory::BulletDetonatePreCheck(BulletClass* pBullet) if (this->ShouldDetonate) return true; - const ParabolaTrajectoryType* const pType = this->Type; + const auto pType = this->Type; if (pType->DetonationHeight >= 0 && pBullet->Velocity.Z < 1e-10 && (pBullet->Location.Z - pBullet->SourceCoords.Z) < pType->DetonationHeight) return true; @@ -1027,7 +1033,7 @@ bool ParabolaTrajectory::BulletDetonatePreCheck(BulletClass* pBullet) } else if (abs(pType->DetonationAngle) < 90.0) { - const double horizontalVelocity = Vector2D{ pBullet->Velocity.X, pBullet->Velocity.Y }.Magnitude(); + const auto horizontalVelocity = Vector2D{ pBullet->Velocity.X, pBullet->Velocity.Y }.Magnitude(); if (horizontalVelocity > 1e-10) { @@ -1051,19 +1057,19 @@ bool ParabolaTrajectory::BulletDetonateLastCheck(BulletClass* pBullet, CellClass pBullet->Velocity.Z -= gravity; const CoordStruct velocityCoords { static_cast(pBullet->Velocity.X), static_cast(pBullet->Velocity.Y), static_cast(pBullet->Velocity.Z) }; - const CoordStruct futureCoords = pBullet->Location + velocityCoords; + const auto futureCoords = pBullet->Location + velocityCoords; if (this->NeedExtraCheck) { - const CellStruct cellDist = CellClass::Coord2Cell(pBullet->Location) - CellClass::Coord2Cell(futureCoords); - const CellStruct cellPace = CellStruct { static_cast(std::abs(cellDist.X)), static_cast(std::abs(cellDist.Y)) }; - const size_t largePace = static_cast(std::max(cellPace.X, cellPace.Y)); - const CoordStruct stepCoord = largePace ? velocityCoords * (1.0 / largePace) : CoordStruct::Empty; - CoordStruct curCoord = pBullet->Location + stepCoord; + const auto cellDist = CellClass::Coord2Cell(pBullet->Location) - CellClass::Coord2Cell(futureCoords); + const auto cellPace = CellStruct { static_cast(std::abs(cellDist.X)), static_cast(std::abs(cellDist.Y)) }; + const auto largePace = static_cast(std::max(cellPace.X, cellPace.Y)); + const auto stepCoord = largePace ? velocityCoords * (1.0 / largePace) : CoordStruct::Empty; + auto curCoord = pBullet->Location + stepCoord; for (size_t i = 1; i <= largePace; ++i) { - const int cellHeight = MapClass::Instance->GetCellFloorHeight(curCoord); + const auto cellHeight = MapClass::Instance->GetCellFloorHeight(curCoord); if (curCoord.Z < cellHeight) { @@ -1088,7 +1094,7 @@ bool ParabolaTrajectory::BulletDetonateLastCheck(BulletClass* pBullet, CellClass } else { - const int cellHeight = MapClass::Instance->GetCellFloorHeight(futureCoords); + const auto cellHeight = MapClass::Instance->GetCellFloorHeight(futureCoords); if (cellHeight < futureCoords.Z) return false; From fd85bf26431f3241218f737f63c3797ff8b4ccbe Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Wed, 27 Nov 2024 18:31:07 +0800 Subject: [PATCH 25/32] Clear up --- src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp index f9a21d6afa..67060d4608 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp @@ -1,9 +1,11 @@ #include "ParabolaTrajectory.h" + +#include +#include + #include #include #include -#include -#include std::unique_ptr ParabolaTrajectoryType::CreateInstance() const { From b444626eb11f17e0abaa9d79d39946b6b91581d2 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Fri, 27 Dec 2024 02:29:38 +0800 Subject: [PATCH 26/32] Fix doc --- docs/New-or-Enhanced-Logics.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index e5db0100b4..32871d8b60 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -35,7 +35,7 @@ This page describes all the engine features that are either new and introduced b - `Tint.VisibleToHouses` can be used to control which houses can see the tint effect. - `FirepowerMultiplier`, `ArmorMultiplier`, `SpeedMultiplier` and `ROFMultiplier` can be used to modify the object's firepower, armor strength, movement speed and weapon reload rate, respectively. - `ArmorMultiplier.AllowWarheads` and `ArmorMultiplier.DisallowWarheads` can be used to restrict which Warheads the armor multiplier is applied to when dealing damage. - - If `ROFMultiplier.ApplyOnCurrentTimer` is set to true, `ROFMultiplier` is applied on currently running reload timer (if any) when the effect is first applied. + - If `ROFMultiplier.ApplyOnCurrentTimer` is set to true, `ROFMultiplier` is applied on currently running reload timer (if any) when the effect is first applied. - If `Cloakable` is set to true, the object the effect is attached to is granted ability to cloak itself for duration of the effect. - `ForceDecloak`, if set to true, will uncloak and make the object the effect is attached to unable to cloak itself for duration of the effect. - `WeaponRange.Multiplier` and `WeaponRange.ExtraRange` can be used to multiply the weapon firing range of the object the effect is attached to, or give it an increase / decrease (measured in cells), respectively. `ExtraRange` is cumulatively applied from all attached effects after all `Multiplier` values have been applied. @@ -798,6 +798,7 @@ Trajectory.Bombard.Height=0.0 ; double In `rulesmd.ini`: ```ini +[SOMEPROJECTILE] ; Projectile Trajectory=Parabola ; Trajectory type Trajectory.Parabola.DetonationDistance=0.4 ; floating point value Trajectory.Parabola.TargetSnapDistance=0.5 ; floating point value From 8ddf1de0371b2e5d783ce249d9ae89c7513fb31a Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Fri, 27 Dec 2024 17:03:16 +0800 Subject: [PATCH 27/32] Remove useless sanity check --- src/Ext/Bullet/Hooks.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Ext/Bullet/Hooks.cpp b/src/Ext/Bullet/Hooks.cpp index 47d4246edb..39730a4108 100644 --- a/src/Ext/Bullet/Hooks.cpp +++ b/src/Ext/Bullet/Hooks.cpp @@ -482,11 +482,8 @@ DEFINE_HOOK(0x415F25, AircraftClass_Fire_TrajectorySkipInertiaEffect, 0x6) GET(BulletClass*, pThis, ESI); - if (auto const pExt = BulletExt::ExtMap.Find(pThis)) - { - if (pExt->Trajectory) - return SkipCheck; - } + if (BulletExt::ExtMap.Find(pThis)->Trajectory) + return SkipCheck; return 0; } From 47b0a25e80779f92418de15cccd97524751da165 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Sat, 28 Dec 2024 00:33:39 +0800 Subject: [PATCH 28/32] Synchronize --- src/Ext/Bullet/Hooks.cpp | 56 ++++++++++++---------------------------- 1 file changed, 16 insertions(+), 40 deletions(-) diff --git a/src/Ext/Bullet/Hooks.cpp b/src/Ext/Bullet/Hooks.cpp index 39730a4108..b802e8deea 100644 --- a/src/Ext/Bullet/Hooks.cpp +++ b/src/Ext/Bullet/Hooks.cpp @@ -290,6 +290,12 @@ DEFINE_HOOK(0x46902C, BulletClass_Explode_Cluster, 0x6) return SkipGameCode; } +constexpr bool CheckTrajectoryCanNotAlwaysSnap(const TrajectoryFlag flag) +{ + return flag == TrajectoryFlag::Straight + || flag == TrajectoryFlag::Parabola; +} + DEFINE_HOOK(0x467CCA, BulletClass_AI_TargetSnapChecks, 0x6) { enum { SkipChecks = 0x467CDE }; @@ -304,15 +310,10 @@ DEFINE_HOOK(0x467CCA, BulletClass_AI_TargetSnapChecks, 0x6) } else if (auto const pExt = BulletAITemp::ExtData) { - if (auto const pTrajectory = pExt->Trajectory.get()) + if (pExt->Trajectory && CheckTrajectoryCanNotAlwaysSnap(pExt->Trajectory->Flag())) { - const TrajectoryFlag flag = pTrajectory->Flag(); - - if (flag == TrajectoryFlag::Straight || flag == TrajectoryFlag::Parabola) - { - R->EAX(pThis->Type); - return SkipChecks; - } + R->EAX(pThis->Type); + return SkipChecks; } } @@ -337,18 +338,10 @@ DEFINE_HOOK(0x468E61, BulletClass_Explode_TargetSnapChecks1, 0x6) } else if (auto const pExt = BulletExt::ExtMap.Find(pThis)) { - if (!pExt->SnappedToTarget) + if (pExt->Trajectory && CheckTrajectoryCanNotAlwaysSnap(pExt->Trajectory->Flag()) && !pExt->SnappedToTarget) { - if (auto const pTrajectory = pExt->Trajectory.get()) - { - const TrajectoryFlag flag = pTrajectory->Flag(); - - if (flag == TrajectoryFlag::Straight || flag == TrajectoryFlag::Parabola) - { - R->EAX(pThis->Type); - return SkipChecks; - } - } + R->EAX(pThis->Type); + return SkipChecks; } } @@ -376,18 +369,8 @@ DEFINE_HOOK(0x468E9F, BulletClass_Explode_TargetSnapChecks2, 0x6) // Fixes issues with walls etc. if (auto const pExt = BulletExt::ExtMap.Find(pThis)) { - if (!pExt->SnappedToTarget) - { - if (auto const pTrajectory = pExt->Trajectory.get()) - { - const TrajectoryFlag flag = pTrajectory->Flag(); - - if (flag == TrajectoryFlag::Straight || flag == TrajectoryFlag::Parabola) - { - return SkipSetCoordinate; - } - } - } + if (pExt->Trajectory && CheckTrajectoryCanNotAlwaysSnap(pExt->Trajectory->Flag()) && !pExt->SnappedToTarget) + return SkipSetCoordinate; } return 0; @@ -401,15 +384,8 @@ DEFINE_HOOK(0x468D3F, BulletClass_ShouldExplode_AirTarget, 0x6) if (auto const pExt = BulletExt::ExtMap.Find(pThis)) { - if (auto const pTrajectory = pExt->Trajectory.get()) - { - const TrajectoryFlag flag = pTrajectory->Flag(); - - if (flag == TrajectoryFlag::Straight || flag == TrajectoryFlag::Parabola) - { - return SkipCheck; - } - } + if (pExt->Trajectory && CheckTrajectoryCanNotAlwaysSnap(pExt->Trajectory->Flag())) + return SkipCheck; } return 0; From fdfbc4ecfde6cbf665a625bc8fd5e7c21bc29e8b Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Sun, 29 Dec 2024 20:22:03 +0800 Subject: [PATCH 29/32] Synchronize --- .../Trajectories/ParabolaTrajectory.cpp | 25 ++++++++++--------- .../Bullet/Trajectories/PhobosTrajectory.cpp | 5 +--- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp index 67060d4608..4a6449ff5f 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp @@ -54,6 +54,7 @@ bool ParabolaTrajectoryType::Save(PhobosStreamWriter& Stm) const void ParabolaTrajectoryType::Read(CCINIClass* const pINI, const char* pSection) { INI_EX exINI(pINI); + this->DetonationDistance.Read(exINI, pSection, "Trajectory.Parabola.DetonationDistance"); this->TargetSnapDistance.Read(exINI, pSection, "Trajectory.Parabola.TargetSnapDistance"); @@ -265,7 +266,7 @@ void ParabolaTrajectory::PrepareForOpenFire(BulletClass* pBullet) else this->CalculateBulletVelocityRightNow(pBullet, &theSourceCoords, gravity); - if (!this->UseDisperseBurst && abs(pType->RotateCoord) > 1e-10 && this->CountOfBurst > 1) + if (!this->UseDisperseBurst && std::abs(pType->RotateCoord) > 1e-10 && this->CountOfBurst > 1) { const auto axis = pType->AxisOfRotation.Get(); @@ -278,7 +279,7 @@ void ParabolaTrajectory::PrepareForOpenFire(BulletClass* pBullet) const auto rotationAxisLengthSquared = rotationAxis.MagnitudeSquared(); - if (abs(rotationAxisLengthSquared) > 1e-10) + if (std::abs(rotationAxisLengthSquared) > 1e-10) { double extraRotate = 0.0; rotationAxis *= 1 / sqrt(rotationAxisLengthSquared); @@ -688,7 +689,7 @@ double ParabolaTrajectory::SearchVelocity(double horizontalDistance, int distanc { // Estimate initial velocity const auto mult = Math::sin(2 * radian); - auto velocity = abs(mult) > 1e-10 ? sqrt(horizontalDistance * gravity / mult) : 0.0; + auto velocity = std::abs(mult) > 1e-10 ? sqrt(horizontalDistance * gravity / mult) : 0.0; velocity += distanceCoordsZ / gravity; velocity = velocity > 8.0 ? velocity : 8.0; const auto error = velocity / 16; @@ -704,7 +705,7 @@ double ParabolaTrajectory::SearchVelocity(double horizontalDistance, int distanc const auto dDifferential = (this->CheckVelocityEquation(horizontalDistance, distanceCoordsZ, (velocity + delta), radian, gravity) - differential) / delta; // Check unacceptable divisor - if (abs(dDifferential) < 1e-10) + if (std::abs(dDifferential) < 1e-10) return velocity; // Calculate the speed of the next iteration @@ -712,7 +713,7 @@ double ParabolaTrajectory::SearchVelocity(double horizontalDistance, int distanc const auto velocityNew = velocity - difference; // Check tolerable error - if (abs(difference) < error) + if (std::abs(difference) < error) return velocityNew; // Update the speed @@ -786,13 +787,13 @@ double ParabolaTrajectory::SearchFixedHeightMeetTime(CoordStruct* pSourceCrd, Co const auto differential = this->CheckFixedHeightEquation(pSourceCrd, pTargetCrd, pOffsetCrd, meetTime, gravity); const auto dDifferential = (this->CheckFixedHeightEquation(pSourceCrd, pTargetCrd, pOffsetCrd, (meetTime + delta), gravity) - differential) / delta; - if (abs(dDifferential) < 1e-10) + if (std::abs(dDifferential) < 1e-10) return meetTime; const auto difference = differential / dDifferential; const auto meetTimeNew = meetTime - difference; - if (abs(difference) < 1.0) + if (std::abs(difference) < 1.0) return meetTimeNew; meetTime = meetTimeNew; @@ -824,13 +825,13 @@ double ParabolaTrajectory::SearchFixedAngleMeetTime(CoordStruct* pSourceCrd, Coo const auto differential = this->CheckFixedAngleEquation(pSourceCrd, pTargetCrd, pOffsetCrd, meetTime, radian, gravity); const auto dDifferential = (this->CheckFixedAngleEquation(pSourceCrd, pTargetCrd, pOffsetCrd, (meetTime + delta), radian, gravity) - differential) / delta; - if (abs(dDifferential) < 1e-10) + if (std::abs(dDifferential) < 1e-10) return meetTime; const auto difference = differential / dDifferential; const auto meetTimeNew = meetTime - difference; - if (abs(difference) < 1.0) + if (std::abs(difference) < 1.0) return meetTimeNew; meetTime = meetTimeNew; @@ -1028,12 +1029,12 @@ bool ParabolaTrajectory::BulletDetonatePreCheck(BulletClass* pBullet) if (pType->DetonationHeight >= 0 && pBullet->Velocity.Z < 1e-10 && (pBullet->Location.Z - pBullet->SourceCoords.Z) < pType->DetonationHeight) return true; - if (abs(pType->DetonationAngle) < 1e-10) + if (std::abs(pType->DetonationAngle) < 1e-10) { if (pBullet->Velocity.Z < 1e-10) return true; } - else if (abs(pType->DetonationAngle) < 90.0) + else if (std::abs(pType->DetonationAngle) < 90.0) { const auto horizontalVelocity = Vector2D{ pBullet->Velocity.X, pBullet->Velocity.Y }.Magnitude(); @@ -1105,7 +1106,7 @@ bool ParabolaTrajectory::BulletDetonateLastCheck(BulletClass* pBullet, CellClass return true; this->LastVelocity = pBullet->Velocity; - this->BulletDetonateEffectuate(pBullet, abs((pBullet->Location.Z - cellHeight) / pBullet->Velocity.Z)); + this->BulletDetonateEffectuate(pBullet, std::abs((pBullet->Location.Z - cellHeight) / pBullet->Velocity.Z)); } return false; diff --git a/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp b/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp index 11da88fd6f..34a535b818 100644 --- a/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp @@ -67,10 +67,7 @@ void TrajectoryTypePointer::LoadFromINI(CCINIClass* pINI, const char* pSection) if (_ptr) { _ptr->Trajectory_Speed.Read(exINI, pSection, "Trajectory.Speed"); - - if (abs(_ptr->Trajectory_Speed) < 1e-10) - _ptr->Trajectory_Speed = 0.001; - + _ptr->Trajectory_Speed = Math::max(0.001,_ptr->Trajectory_Speed); _ptr->Read(pINI, pSection); } } From 32c90bded6f720ad15594d11a32c5f24e11b0fcd Mon Sep 17 00:00:00 2001 From: Noble Fish <89088785+DeathFishAtEase@users.noreply.github.com> Date: Mon, 3 Feb 2025 04:12:50 +0800 Subject: [PATCH 30/32] ")" --- docs/Fixed-or-Improved-Logics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index d39932ba2b..5606546923 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -1124,7 +1124,7 @@ Palette= ; filename - excluding .pal extension and three-character the - Instead of showing at 1 point of HP left, TerrainTypes switch to damaged frames once their health reaches `[AudioVisual]` -> `ConditionYellow.Terrain` percentage of their maximum health. Defaults to `ConditionYellow` if not set. - In addition, TerrainTypes can now show 'crumbling' animation after their health has reached zero and before they are deleted from the map by setting `HasCrumblingFrames` to true. - Crumbling frames start from first frame after both regular & damaged frames and ends at halfway point of the frames in TerrainType's image. - - Note that the number of regular & damage frames considered for this depends on value of `HasDamagedFrames` and for `IsAnimated` TerrainTypes, `AnimationLength` (see [Animated TerrainTypes](#animated-terraintypes). Exercise caution and ensure there are correct amount of frames to display. + - Note that the number of regular & damage frames considered for this depends on value of `HasDamagedFrames` and for `IsAnimated` TerrainTypes, `AnimationLength` (see [Animated TerrainTypes](#animated-terraintypes)). Exercise caution and ensure there are correct amount of frames to display. - Sound event from `CrumblingSound` (if set) is played when crumbling animation starts playing. - [Destroy animation & sound](New-or-Enhanced-Logics.md#destroy-animation--sound) only play after crumbling animation has finished. From 9eeed899b27c53a203e6cfd7fee869d988ba0efe Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Mon, 3 Feb 2025 17:19:18 +0800 Subject: [PATCH 31/32] Fix merge --- docs/Whats-New.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index eddae83a50..958bcc3913 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -318,6 +318,7 @@ New: - Custom exit cell for infantry factory (by Starkku) - Option for vehicles to keep target when issued move command (by Starkku) - Skip anim delay for burst fire (by TaranDahl) +- New Parabola trajectory (by CrimRecya) Vanilla fixes: - Aircraft will now behave as expected according to it's `MovementZone` and `SpeedType` when moving onto different surfaces. In particular, this fixes erratic behavior when vanilla aircraft is ordered to move onto water surface and instead the movement order changes to a shore nearby (by CrimRecya) @@ -476,7 +477,6 @@ New: - Custom object palettes for TerrainTypes (by Starkku) - Forbidding parallel AI queues for specific TechnoTypes (by Starkku) - Nonprovocative Warheads (by Starkku) -- New Parabola trajectory (by CrimRecya) - Buildings considered as destroyable pathfinding obstacles (by Starkku) - `FireOnce` infantry sequence reset toggle (by Starkku) - Assign Super Weapon cameo to any sidebar tab (by NetsuNegi) From d17be2acab2ac302ae9a7940fdf32cfe23858d12 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Sat, 8 Feb 2025 19:17:44 +0800 Subject: [PATCH 32/32] Review and add some comment --- .../Trajectories/ParabolaTrajectory.cpp | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp index 4a6449ff5f..3573dea7ff 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp @@ -131,6 +131,7 @@ void ParabolaTrajectory::OnUnlimbo(BulletClass* pBullet, CoordStruct* pCoord, Bu const auto pTarget = abstract_cast(pBullet->Target); bool resetTarget = false; + // Special case: Set the target to the ground if (pType->DetonationDistance.Get() <= -1e-10 && pTarget) { if (const auto pCell = MapClass::Instance->TryGetCellAt(pTarget->GetCoords())) @@ -141,6 +142,7 @@ void ParabolaTrajectory::OnUnlimbo(BulletClass* pBullet, CoordStruct* pCoord, Bu } } + // Record some information, and try to see if mirror offset is needed like Straight if (const auto pWeapon = pBullet->WeaponType) this->CountOfBurst = pWeapon->Burst; @@ -152,6 +154,7 @@ void ParabolaTrajectory::OnUnlimbo(BulletClass* pBullet, CoordStruct* pCoord, Bu this->OffsetCoord.Y = -(this->OffsetCoord.Y); } + // Wait, or launch immediately? if (!pType->LeadTimeCalculate || !pTarget || resetTarget) this->PrepareForOpenFire(pBullet); else @@ -179,6 +182,7 @@ void ParabolaTrajectory::OnAIPreDetonate(BulletClass* pBullet) { const auto targetSnapDistance = this->Type->TargetSnapDistance.Get(); + // Whether to snap to target? if (targetSnapDistance > 0) { const auto pTarget = abstract_cast(pBullet->Target); @@ -193,6 +197,7 @@ void ParabolaTrajectory::OnAIPreDetonate(BulletClass* pBullet) } } + // If the speed is too fast, it may smash through the floor const auto cellHeight = MapClass::Instance->GetCellFloorHeight(pBullet->Location); if (pBullet->Location.Z < cellHeight) @@ -224,6 +229,7 @@ void ParabolaTrajectory::PrepareForOpenFire(BulletClass* pBullet) leadTimeCalculate &= theTargetCoords != this->LastTargetCoord; double rotateAngle = 0.0; + // Calculate the orientation of the coordinate system if (!pType->LeadTimeCalculate && theTargetCoords == theSourceCoords && pBullet->Owner) //For disperse. { const auto theOwnerCoords = pBullet->Owner->GetCoords(); @@ -234,6 +240,7 @@ void ParabolaTrajectory::PrepareForOpenFire(BulletClass* pBullet) rotateAngle = Math::atan2(theTargetCoords.Y - theSourceCoords.Y , theTargetCoords.X - theSourceCoords.X); } + // Add the fixed offset value if (this->OffsetCoord != CoordStruct::Empty) { theTargetCoords.X += static_cast(this->OffsetCoord.X * Math::cos(rotateAngle) + this->OffsetCoord.Y * Math::sin(rotateAngle)); @@ -241,6 +248,7 @@ void ParabolaTrajectory::PrepareForOpenFire(BulletClass* pBullet) theTargetCoords.Z += this->OffsetCoord.Z; } + // Add random offset value if (pBullet->Type->Inaccurate) { const auto pTypeExt = BulletTypeExt::ExtMap.Find(pBullet->Type); @@ -252,6 +260,8 @@ void ParabolaTrajectory::PrepareForOpenFire(BulletClass* pBullet) } pBullet->TargetCoords = theTargetCoords; + + // Non positive gravity is not accepted const auto gravity = BulletTypeExt::GetAdjustedGravity(pBullet->Type); if (gravity <= 1e-10) @@ -261,11 +271,13 @@ void ParabolaTrajectory::PrepareForOpenFire(BulletClass* pBullet) return; } + // Calculate the firing velocity vector of the bullet if (leadTimeCalculate) this->CalculateBulletVelocityLeadTime(pBullet, &theSourceCoords, gravity); else this->CalculateBulletVelocityRightNow(pBullet, &theSourceCoords, gravity); + // Rotate the selected angle if (!this->UseDisperseBurst && std::abs(pType->RotateCoord) > 1e-10 && this->CountOfBurst > 1) { const auto axis = pType->AxisOfRotation.Get(); @@ -871,15 +883,18 @@ bool ParabolaTrajectory::CalculateBulletVelocityAfterBounce(BulletClass* pBullet { const auto pType = this->Type; + // Can bounce on water surface? if (pCell->LandType == LandType::Water && !pType->BounceOnWater) return true; --this->BounceTimes; this->ShouldBounce = false; + // Calculate the velocity vector after bouncing const auto groundNormalVector = this->GetGroundNormalVector(pBullet, pCell); pBullet->Velocity = (this->LastVelocity - groundNormalVector * (this->LastVelocity * groundNormalVector) * 2) * pType->BounceCoefficient; + // Detonate an additional warhead when bouncing? if (pType->BounceDetonate) { const auto pFirer = pBullet->Owner; @@ -887,6 +902,7 @@ bool ParabolaTrajectory::CalculateBulletVelocityAfterBounce(BulletClass* pBullet WarheadTypeExt::DetonateAt(pBullet->WH, pBullet->Location, pFirer, pBullet->Health, pOwner); } + // Calculate the attenuation damage after bouncing if (const int damage = pBullet->Health) { if (const int newDamage = static_cast(damage * pType->BounceAttenuation)) @@ -957,6 +973,7 @@ BulletVelocity ParabolaTrajectory::GetGroundNormalVector(BulletClass* pBullet, C const auto bulletHeight = pBullet->Location.Z; const auto lastCellHeight = MapClass::Instance->GetCellFloorHeight(pBullet->Location - velocityCoords); + // Check if it has hit a cliff if (bulletHeight < cellHeight && (cellHeight - lastCellHeight) > 384) { auto cell = pCell->MapCoords; @@ -1003,6 +1020,7 @@ BulletVelocity ParabolaTrajectory::GetGroundNormalVector(BulletClass* pBullet, C return BulletVelocity{ 0.7071067811865475244008443621049 * reverseSgnX, 0.7071067811865475244008443621049 * reverseSgnY, 0.0 }; } + // Just ordinary ground return BulletVelocity{ 0.0, 0.0, 1.0 }; } @@ -1026,6 +1044,7 @@ bool ParabolaTrajectory::BulletDetonatePreCheck(BulletClass* pBullet) const auto pType = this->Type; + // Check all conditions for premature detonation if (pType->DetonationHeight >= 0 && pBullet->Velocity.Z < 1e-10 && (pBullet->Location.Z - pBullet->SourceCoords.Z) < pType->DetonationHeight) return true; @@ -1049,10 +1068,7 @@ bool ParabolaTrajectory::BulletDetonatePreCheck(BulletClass* pBullet) } } - if (pBullet->TargetCoords.DistanceFrom(pBullet->Location) < pType->DetonationDistance.Get()) - return true; - - return false; + return (pBullet->TargetCoords.DistanceFrom(pBullet->Location) < pType->DetonationDistance.Get()); } bool ParabolaTrajectory::BulletDetonateLastCheck(BulletClass* pBullet, CellClass* pCell, double gravity, bool bounce) @@ -1062,6 +1078,7 @@ bool ParabolaTrajectory::BulletDetonateLastCheck(BulletClass* pBullet, CellClass const CoordStruct velocityCoords { static_cast(pBullet->Velocity.X), static_cast(pBullet->Velocity.Y), static_cast(pBullet->Velocity.Z) }; const auto futureCoords = pBullet->Location + velocityCoords; + // Check all the cells that the next frame passes through like Straight if (this->NeedExtraCheck) { const auto cellDist = CellClass::Coord2Cell(pBullet->Location) - CellClass::Coord2Cell(futureCoords); @@ -1072,6 +1089,7 @@ bool ParabolaTrajectory::BulletDetonateLastCheck(BulletClass* pBullet, CellClass for (size_t i = 1; i <= largePace; ++i) { + // Below ground level? const auto cellHeight = MapClass::Instance->GetCellFloorHeight(curCoord); if (curCoord.Z < cellHeight) @@ -1084,6 +1102,7 @@ bool ParabolaTrajectory::BulletDetonateLastCheck(BulletClass* pBullet, CellClass break; } + // Impact on the wall? if (pBullet->Type->SubjectToWalls && pCell->OverlayTypeIndex != -1 && OverlayTypeClass::Array->GetItem(pCell->OverlayTypeIndex)->Wall) { pBullet->Velocity *= static_cast(i) / largePace; @@ -1117,6 +1136,7 @@ void ParabolaTrajectory::BulletDetonateEffectuate(BulletClass* pBullet, double v if (velocityMult < 1.0) pBullet->Velocity *= velocityMult; + // Is it detonating or bouncing? if (this->BounceTimes > 0) this->ShouldBounce = true; else