diff --git a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs index 7864ba1464..51cfc13b77 100644 --- a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs +++ b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs @@ -1510,26 +1510,7 @@ protected float GetHeightAtTicks(int ticks) /// Distance in cells that the projectile will fly at the given arc. protected float DistanceTraveled => TrajectoryWorker.DistanceTraveled(shotHeight, shotSpeed, shotAngle, GravityFactor); - /// - /// Calculates the shot angle necessary to reach range with a projectile of speed velocity at a height difference of heightDifference, returning either the upper or lower arc in radians. Does not take into account air resistance. - /// - /// Projectile velocity in cells per second. - /// Cells between shooter and target. - /// Difference between initial shot height and target height in vertical cells. - /// Whether to take the lower (False) or upper (True) arc angle. - /// Arc angle in radians off the ground. - public static float GetShotAngle(float velocity, float range, float heightDifference, bool flyOverhead, float gravity) - { - float squareRootCheck = Mathf.Sqrt(Mathf.Pow(velocity, 4f) - gravity * (gravity * Mathf.Pow(range, 2f) + 2f * heightDifference * Mathf.Pow(velocity, 2f))); - if (float.IsNaN(squareRootCheck)) - { - //Target is too far to hit with given velocity/range/gravity params - //set firing angle for maximum distance - Log.Warning("[CE] Tried to fire projectile to unreachable target cell, truncating to maximum distance."); - return 45.0f * Mathf.Deg2Rad; - } - return Mathf.Atan((Mathf.Pow(velocity, 2f) + (flyOverhead ? 1f : -1f) * squareRootCheck) / (gravity * range)); - } + #endregion protected static Material[] GetShadowMaterial(Graphic_Collection g) diff --git a/Source/CombatExtended/CombatExtended/Projectiles/TrajectoryWorkers/BaseTrajectoryWorker.cs b/Source/CombatExtended/CombatExtended/Projectiles/TrajectoryWorkers/BaseTrajectoryWorker.cs index f686e01864..7db3b7beab 100644 --- a/Source/CombatExtended/CombatExtended/Projectiles/TrajectoryWorkers/BaseTrajectoryWorker.cs +++ b/Source/CombatExtended/CombatExtended/Projectiles/TrajectoryWorkers/BaseTrajectoryWorker.cs @@ -101,5 +101,44 @@ public virtual Vector3 GetVelocity(float shotSpeed, float rotation, float angle) angle = angle * Mathf.Rad2Deg; // transform to degrees return Vector2.up.RotatedBy(rotation).ToVector3().RotatedBy(angle) * shotSpeed / GenTicks.TicksPerRealSecond; } + + /// + /// Shot angle in radians + /// + /// Source shot, including shot height + /// Target position, including target height + /// angle in radians + public virtual float ShotAngle(ProjectilePropertiesCE projectilePropsCE, Vector3 source, Vector3 targetPos, float? velocity = null) + { + var targetHeight = targetPos.y; + var shotHeight = source.y; + var newTargetLoc = new Vector2(targetPos.x, targetPos.z); + var sourceV2 = new Vector2(source.x, source.z); + if (projectilePropsCE.isInstant) + { + return Mathf.Atan2(targetHeight - shotHeight, (newTargetLoc - sourceV2).magnitude); + } + else + { + var _velocity = velocity ?? projectilePropsCE.speed; + var gravity = projectilePropsCE.Gravity; + var heightDifference = targetHeight - shotHeight; + var range = (newTargetLoc - sourceV2).magnitude; + float squareRootCheck = Mathf.Sqrt(Mathf.Pow(_velocity, 4f) - gravity * (gravity * Mathf.Pow(range, 2f) + 2f * heightDifference * Mathf.Pow(_velocity, 2f))); + if (float.IsNaN(squareRootCheck)) + { + //Target is too far to hit with given velocity/range/gravity params + //set firing angle for maximum distance + Log.Warning("[CE] Tried to fire projectile to unreachable target cell, truncating to maximum distance."); + return 45.0f * Mathf.Deg2Rad; + } + return Mathf.Atan((Mathf.Pow(_velocity, 2f) + (projectilePropsCE.flyOverhead ? 1f : -1f) * squareRootCheck) / (gravity * range)); + } + } + public virtual float ShotRotation(ProjectilePropertiesCE projectilePropertiesCE, Vector3 source, Vector3 targetPos) + { + var w = targetPos - source; + return ( - 90 + Mathf.Rad2Deg * Mathf.Atan2(w.z, w.x)) % 360; + } } } diff --git a/Source/CombatExtended/CombatExtended/Verbs/Verb_LaunchProjectileCE.cs b/Source/CombatExtended/CombatExtended/Verbs/Verb_LaunchProjectileCE.cs index 0e4e516aef..b2d0c01c0a 100644 --- a/Source/CombatExtended/CombatExtended/Verbs/Verb_LaunchProjectileCE.cs +++ b/Source/CombatExtended/CombatExtended/Verbs/Verb_LaunchProjectileCE.cs @@ -610,16 +610,7 @@ protected float ShotAngle(Vector3 targetPos) /// angle in radians protected virtual float ShotAngle(Vector3 source, Vector3 targetPos) { - var targetHeight = targetPos.y; - var newTargetLoc = new Vector2(targetPos.x, targetPos.z); - if (projectilePropsCE.isInstant) - { - return Mathf.Atan2(targetHeight - ShotHeight, (newTargetLoc - sourceLoc).magnitude); - } - else - { - return ProjectileCE.GetShotAngle(ShotSpeed, (newTargetLoc - sourceLoc).magnitude, targetHeight - ShotHeight, Projectile.projectile.flyOverhead, projectilePropsCE.Gravity); - } + return projectilePropsCE.TrajectoryWorker.ShotAngle(projectilePropsCE, source, targetPos); } protected float ShotRotation(Vector3 targetPos) { diff --git a/Source/CombatExtended/CombatExtended/WorldObjects/TravelingShell.cs b/Source/CombatExtended/CombatExtended/WorldObjects/TravelingShell.cs index ce5a108318..01ca8b45d4 100644 --- a/Source/CombatExtended/CombatExtended/WorldObjects/TravelingShell.cs +++ b/Source/CombatExtended/CombatExtended/WorldObjects/TravelingShell.cs @@ -151,20 +151,18 @@ private bool TryShell(WorldObject worldObject) private void LaunchProjectile(IntVec3 sourceCell, LocalTargetInfo target, Map map, float shotSpeed = 20, float shotHeight = 200) { - IntVec3 targetCell = target.Cell; - Vector2 source = new Vector2(sourceCell.x, sourceCell.z); - Vector2 destination = new Vector2(targetCell.x, targetCell.z); - Vector2 w = (destination - source); + Vector3 source = new Vector3(sourceCell.x, shotHeight, sourceCell.z); + Vector3 targetPos = target.Cell.ToVector3Shifted(); ProjectileCE projectile = (ProjectileCE)ThingMaker.MakeThing(shellDef); ProjectilePropertiesCE pprops = projectile.def.projectile as ProjectilePropertiesCE; - float shotRotation = (-90 + Mathf.Rad2Deg * Mathf.Atan2(w.y, w.x)) % 360; - float shotAngle = ProjectileCE.GetShotAngle(shotSpeed, (destination - source).magnitude, -shotHeight, false, pprops.Gravity); + float shotRotation = pprops.TrajectoryWorker.ShotRotation(pprops, source, targetPos); + float shotAngle = pprops.TrajectoryWorker.ShotAngle(pprops, source, targetPos, shotSpeed); projectile.canTargetSelf = false; projectile.Position = sourceCell; projectile.SpawnSetup(map, false); - projectile.Launch(launcher, source, shotAngle, shotRotation, shotHeight, shotSpeed); + projectile.Launch(launcher, new Vector2(source.x, source.z), shotAngle, shotRotation, shotHeight, shotSpeed); //projectile.cameraShakingInit = Rand.Range(0f, 2f); } diff --git a/Source/CombatExtended/Harmony/Harmony_CompAbilityEffect_LaunchProjectile.cs b/Source/CombatExtended/Harmony/Harmony_CompAbilityEffect_LaunchProjectile.cs index 55bc11d5cc..ad41171f69 100644 --- a/Source/CombatExtended/Harmony/Harmony_CompAbilityEffect_LaunchProjectile.cs +++ b/Source/CombatExtended/Harmony/Harmony_CompAbilityEffect_LaunchProjectile.cs @@ -24,26 +24,14 @@ internal static bool Prefix(CompAbilityEffect_LaunchProjectile __instance, Local if (projectileDef.projectile is ProjectilePropertiesCE ppce) { Pawn pawn = __instance.parent.pawn; - var u = pawn.TrueCenter(); - var sourceLoc = new Vector2(); - sourceLoc.Set(u.x, u.z); - var targetLocation = new Vector2(); + var u = pawn.TrueCenter().WithY((new CollisionVertical(pawn)).shotHeight); + var targetPos = target.Thing != null ? target.Thing.TrueCenter() : target.Cell.ToVector3Shifted(); + targetPos = targetPos.WithY((new CollisionVertical(target.Thing)).shotHeight); - if (target.HasThing) - { - targetLocation.Set(target.Thing.TrueCenter().x, target.Thing.TrueCenter().z); - } - else - { - targetLocation.Set(target.Cell.ToIntVec2.x, target.Cell.ToIntVec2.z); - } - var w = (targetLocation - sourceLoc); - float shotRotation = (-90 + Mathf.Rad2Deg * Mathf.Atan2(w.y, w.x)) % 360; - - var targetVert = new CollisionVertical(target.Thing); - var angle = ProjectileCE.GetShotAngle(ppce.speed, (target.Cell - pawn.Position).LengthHorizontal, targetVert.HeightRange.Average - 1, ppce.flyOverhead, ppce.Gravity); - CE_Utility.LaunchProjectileCE(projectileDef, sourceLoc, target, pawn, angle, shotRotation, 1, ppce.speed); + var angle = ppce.TrajectoryWorker.ShotAngle(ppce, u, targetPos); + float shotRotation = ppce.TrajectoryWorker.ShotRotation(ppce, u, targetPos); + CE_Utility.LaunchProjectileCE(projectileDef, new Vector2(u.x, u.z), target, pawn, angle, shotRotation, u.y, ppce.speed); return false; } }