Downward lookahead.
Added downward lookahead capability.
This commit is contained in:
@@ -35,19 +35,19 @@ namespace OpenRA.Mods.Common.Effects
|
|||||||
public readonly bool Shadow = false;
|
public readonly bool Shadow = false;
|
||||||
|
|
||||||
[Desc("Minimum vertical launch angle (pitch).")]
|
[Desc("Minimum vertical launch angle (pitch).")]
|
||||||
public readonly WAngle MinimumLaunchAngle = new WAngle(-128);
|
public readonly WAngle MinimumLaunchAngle = WAngle.Zero;
|
||||||
|
|
||||||
[Desc("Maximum vertical launch angle (pitch).")]
|
[Desc("Maximum vertical launch angle (pitch).")]
|
||||||
public readonly WAngle MaximumLaunchAngle = new WAngle(64);
|
public readonly WAngle MaximumLaunchAngle = new WAngle(64);
|
||||||
|
|
||||||
[Desc("Minimum launch speed in WDist / tick")]
|
[Desc("Minimum launch speed in WDist / tick")]
|
||||||
public readonly WDist MinimumLaunchSpeed = WDist.Zero;
|
public readonly WDist MinimumLaunchSpeed = new WDist(75);
|
||||||
|
|
||||||
[Desc("Maximum launch speed in WDist / tick")]
|
[Desc("Maximum launch speed in WDist / tick")]
|
||||||
public readonly WDist MaximumLaunchSpeed = new WDist(8);
|
public readonly WDist MaximumLaunchSpeed = new WDist(200);
|
||||||
|
|
||||||
[Desc("Maximum projectile speed in WDist / tick")]
|
[Desc("Maximum projectile speed in WDist / tick")]
|
||||||
public readonly WDist MaximumSpeed = new WDist(512);
|
public readonly WDist MaximumSpeed = new WDist(384);
|
||||||
|
|
||||||
[Desc("Projectile acceleration when propulsion activated.")]
|
[Desc("Projectile acceleration when propulsion activated.")]
|
||||||
public readonly WDist Acceleration = new WDist(5);
|
public readonly WDist Acceleration = new WDist(5);
|
||||||
@@ -128,6 +128,7 @@ namespace OpenRA.Mods.Common.Effects
|
|||||||
public IEffect Create(ProjectileArgs args) { return new Missile(this, args); }
|
public IEffect Create(ProjectileArgs args) { return new Missile(this, args); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: double check square roots!!!
|
||||||
public class Missile : IEffect, ISync
|
public class Missile : IEffect, ISync
|
||||||
{
|
{
|
||||||
enum States
|
enum States
|
||||||
@@ -153,6 +154,7 @@ namespace OpenRA.Mods.Common.Effects
|
|||||||
States state;
|
States state;
|
||||||
bool targetPassedBy;
|
bool targetPassedBy;
|
||||||
bool lockOn = false;
|
bool lockOn = false;
|
||||||
|
bool allowPassBy; // TODO: use this also with high minimum launch angle settings
|
||||||
|
|
||||||
WPos targetPosition;
|
WPos targetPosition;
|
||||||
WVec offset;
|
WVec offset;
|
||||||
@@ -191,7 +193,7 @@ namespace OpenRA.Mods.Common.Effects
|
|||||||
|
|
||||||
DetermineLaunchSpeedAndAngle(world, out speed, out vFacing);
|
DetermineLaunchSpeedAndAngle(world, out speed, out vFacing);
|
||||||
|
|
||||||
velocity = new WVec(0, -info.MaximumLaunchSpeed.Length, 0)
|
velocity = new WVec(0, -speed, 0)
|
||||||
.Rotate(new WRot(WAngle.FromFacing(vFacing), WAngle.Zero, WAngle.Zero))
|
.Rotate(new WRot(WAngle.FromFacing(vFacing), WAngle.Zero, WAngle.Zero))
|
||||||
.Rotate(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(hFacing)));
|
.Rotate(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(hFacing)));
|
||||||
|
|
||||||
@@ -218,67 +220,76 @@ namespace OpenRA.Mods.Common.Effects
|
|||||||
static int LoopRadius(int speed, int rot)
|
static int LoopRadius(int speed, int rot)
|
||||||
{
|
{
|
||||||
// loopRadius in w-units = speed in w-units per tick / angular speed in radians per tick
|
// loopRadius in w-units = speed in w-units per tick / angular speed in radians per tick
|
||||||
// angular speed in radians per tick = VROT in facing units per tick * (pi radians / 128 facing units)
|
// angular speed in radians per tick = rot in facing units per tick * (pi radians / 128 facing units)
|
||||||
// pi = 314 / 100
|
// pi = 314 / 100
|
||||||
// ==> loopRadius = (speed * 128 * 100) / (314 * VROT)
|
// ==> loopRadius = (speed * 128 * 100) / (314 * rot)
|
||||||
return (speed * 6400) / (157 * rot);
|
return (speed * 6400) / (157 * rot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DetermineLaunchSpeedAndAngleForIncline(int predClfDist, int diffClfMslHgt, int relTarHorDist,
|
||||||
|
out int speed, out int vFacing)
|
||||||
|
{
|
||||||
|
speed = info.MaximumLaunchSpeed.Length;
|
||||||
|
|
||||||
|
// Find smallest vertical facing, for which the missile will be able to climb terrAltDiff w-units
|
||||||
|
// within hHeightChange w-units all the while ending the ascent with vertical facing 0
|
||||||
|
vFacing = info.MaximumLaunchAngle.Angle >> 2;
|
||||||
|
|
||||||
|
// Compute minimum speed necessary to both be able to face directly upwards and have enough space
|
||||||
|
// to hit the target without passing it by (and thus having to do horizontal loops)
|
||||||
|
var minSpeed = ((System.Math.Min(predClfDist * 1024 / (1024 - WAngle.FromFacing(vFacing).Sin()),
|
||||||
|
(relTarHorDist + predClfDist) * 1024 / (2 * (2048 - WAngle.FromFacing(vFacing).Sin())))
|
||||||
|
* info.VerticalRateOfTurn * 157) / 6400).Clamp(info.MinimumLaunchSpeed.Length, info.MaximumLaunchSpeed.Length);
|
||||||
|
|
||||||
|
if ((sbyte)vFacing < 0)
|
||||||
|
speed = minSpeed;
|
||||||
|
else if (!WillClimbWithinDistance(vFacing, loopRadius, predClfDist, diffClfMslHgt)
|
||||||
|
&& !WillClimbAroundInclineTop(vFacing, loopRadius, predClfDist, diffClfMslHgt, speed))
|
||||||
|
{
|
||||||
|
// Find highest speed greater than the above minimum that allows the missile
|
||||||
|
// to surmount the incline
|
||||||
|
var vFac = vFacing;
|
||||||
|
speed = BisectionSearch(minSpeed, info.MaximumLaunchSpeed.Length, spd =>
|
||||||
|
{
|
||||||
|
var lpRds = LoopRadius(spd, info.VerticalRateOfTurn);
|
||||||
|
return WillClimbWithinDistance(vFac, lpRds, predClfDist, diffClfMslHgt)
|
||||||
|
|| WillClimbAroundInclineTop(vFac, lpRds, predClfDist, diffClfMslHgt, spd);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Find least vertical facing that will allow the missile to climb
|
||||||
|
// terrAltDiff w-units within hHeightChange w-units
|
||||||
|
// all the while ending the ascent with vertical facing 0
|
||||||
|
vFacing = BisectionSearch(System.Math.Max((sbyte)(info.MinimumLaunchAngle.Angle >> 2), (sbyte)0),
|
||||||
|
(sbyte)(info.MaximumLaunchAngle.Angle >> 2),
|
||||||
|
vFac => !WillClimbWithinDistance(vFac, loopRadius, predClfDist, diffClfMslHgt)) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Double check Launch parameter determination
|
||||||
void DetermineLaunchSpeedAndAngle(World world, out int speed, out int vFacing)
|
void DetermineLaunchSpeedAndAngle(World world, out int speed, out int vFacing)
|
||||||
{
|
{
|
||||||
speed = info.MaximumLaunchSpeed.Length;
|
speed = info.MaximumLaunchSpeed.Length;
|
||||||
var loopRadius = LoopRadius(speed, info.VerticalRateOfTurn);
|
loopRadius = LoopRadius(speed, info.VerticalRateOfTurn);
|
||||||
|
|
||||||
// Compute current distance from target position
|
// Compute current distance from target position
|
||||||
var tarDistVec = targetPosition + offset - pos;
|
var tarDistVec = targetPosition + offset - pos;
|
||||||
var relTarHorDist = tarDistVec.HorizontalLength;
|
var relTarHorDist = tarDistVec.HorizontalLength;
|
||||||
|
|
||||||
int predClfHgt;
|
int predClfHgt, predClfDist, lastHtChg, lastHt;
|
||||||
int predClfDist;
|
InclineLookahead(world, relTarHorDist, out predClfHgt, out predClfDist, out lastHtChg, out lastHt);
|
||||||
InclineLookahead(world, relTarHorDist, out predClfHgt, out predClfDist);
|
|
||||||
|
|
||||||
// Height difference between the incline height and missile height
|
// Height difference between the incline height and missile height
|
||||||
var diffClfMslHgt = predClfHgt - pos.Z;
|
var diffClfMslHgt = predClfHgt - pos.Z;
|
||||||
|
|
||||||
// Incline coming up
|
// Incline coming up
|
||||||
if (diffClfMslHgt >= 0)
|
if (diffClfMslHgt >= 0)
|
||||||
|
DetermineLaunchSpeedAndAngleForIncline(predClfDist, diffClfMslHgt, relTarHorDist, out speed, out vFacing);
|
||||||
|
else if (lastHt != 0)
|
||||||
{
|
{
|
||||||
// Find smallest vertical facing, attainable in the next tick,
|
vFacing = System.Math.Max((sbyte)(info.MinimumLaunchAngle.Angle >> 2), (sbyte)0);
|
||||||
// for which the missile will be able to climb terrAltDiff w-units
|
speed = info.MaximumLaunchSpeed.Length;
|
||||||
// within hHeightChange w-units all the while ending the ascent
|
|
||||||
// with vertical facing 0
|
|
||||||
vFacing = info.MaximumLaunchAngle.Angle >> 2;
|
|
||||||
|
|
||||||
// Compute minimum speed necessary to both be able to face directly upwards and
|
|
||||||
// have enough space to hit the target without passing it by (and thus having to
|
|
||||||
// do horizontal loops)
|
|
||||||
var minSpeed = ((System.Math.Min(predClfDist * 1024 / (1024 - WAngle.FromFacing(vFacing).Sin()),
|
|
||||||
(relTarHorDist + predClfDist) * 1024 / (2 * (2048 - WAngle.FromFacing(vFacing).Sin())))
|
|
||||||
* info.VerticalRateOfTurn * 157) / 6400).Clamp(info.MinimumLaunchSpeed.Length, info.MaximumLaunchSpeed.Length);
|
|
||||||
|
|
||||||
if ((sbyte)vFacing < 0)
|
|
||||||
speed = minSpeed;
|
|
||||||
else if (!WillClimbWithinDistance(vFacing, loopRadius, predClfDist, diffClfMslHgt)
|
|
||||||
&& !WillClimbAroundInclineTop(vFacing, loopRadius, predClfDist, diffClfMslHgt, speed))
|
|
||||||
{
|
|
||||||
// Find highest speed greater than the above minimum that allows the missile
|
|
||||||
// to surmount the incline
|
|
||||||
var vFac = vFacing;
|
|
||||||
speed = BisectionSearch(minSpeed, info.MaximumLaunchSpeed.Length, spd =>
|
|
||||||
{
|
|
||||||
var lpRds = LoopRadius(spd, info.VerticalRateOfTurn);
|
|
||||||
return WillClimbWithinDistance(vFac, lpRds, predClfDist, diffClfMslHgt)
|
|
||||||
|| WillClimbAroundInclineTop(vFac, lpRds, predClfDist, diffClfMslHgt, spd);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Find least vertical facing that will allow the missile to climb
|
|
||||||
// terrAltDiff w-units within hHeightChange w-units
|
|
||||||
// all the while ending the ascent with vertical facing 0
|
|
||||||
vFacing = BisectionSearch(0, info.MaximumLaunchAngle.Angle,
|
|
||||||
vFac => !WillClimbWithinDistance(vFac, loopRadius, predClfDist, diffClfMslHgt)) + 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -299,13 +310,13 @@ namespace OpenRA.Mods.Common.Effects
|
|||||||
|
|
||||||
// Will missile be able to climb terrAltDiff w-units within hHeightChange w-units
|
// Will missile be able to climb terrAltDiff w-units within hHeightChange w-units
|
||||||
// all the while ending the ascent with vertical facing 0
|
// all the while ending the ascent with vertical facing 0
|
||||||
// Calling this function only makes sense when vFacing is nonzero
|
// Calling this function only makes sense when vFacing is nonnegative
|
||||||
static bool WillClimbWithinDistance(int vFacing, int loopRadius, int predClfDist, int diffClfMslHgt)
|
static bool WillClimbWithinDistance(int vFacing, int loopRadius, int predClfDist, int diffClfMslHgt)
|
||||||
{
|
{
|
||||||
// Missile's horizontal distance from loop's center
|
// Missile's horizontal distance from loop's center
|
||||||
var missDist = loopRadius * WAngle.FromFacing(vFacing).Sin() / 1024;
|
var missDist = loopRadius * WAngle.FromFacing(vFacing).Sin() / 1024;
|
||||||
|
|
||||||
// Missile's height above loop's center
|
// Missile's height below loop's top
|
||||||
var missHgt = loopRadius * (1024 - WAngle.FromFacing(vFacing).Cos()) / 1024;
|
var missHgt = loopRadius * (1024 - WAngle.FromFacing(vFacing).Cos()) / 1024;
|
||||||
|
|
||||||
// Height that would be climbed without changing vertical facing
|
// Height that would be climbed without changing vertical facing
|
||||||
@@ -392,13 +403,15 @@ namespace OpenRA.Mods.Common.Effects
|
|||||||
|
|
||||||
// NOTE: It might be desirable to make lookahead more intelligent by outputting more information
|
// NOTE: It might be desirable to make lookahead more intelligent by outputting more information
|
||||||
// than just the highest point in the lookahead distance
|
// than just the highest point in the lookahead distance
|
||||||
void InclineLookahead(World world, int distCheck, out int predClfHgt, out int predClfDist)
|
void InclineLookahead(World world, int distCheck, out int predClfHgt, out int predClfDist, out int lastHtChg, out int lastHt)
|
||||||
{
|
{
|
||||||
predClfHgt = 0; // Highest probed terrain height
|
predClfHgt = 0; // Highest probed terrain height
|
||||||
predClfDist = 0; // Distance from highest point
|
predClfDist = 0; // Distance from highest point
|
||||||
|
lastHtChg = 0; // Distance from last time the height changes
|
||||||
|
lastHt = 0; // Height just before the last height change
|
||||||
|
|
||||||
// NOTE: Might be desired to unhardcode the lookahead step size
|
// NOTE: Might be desired to unhardcode the lookahead step size
|
||||||
var stepSize = 128;
|
var stepSize = 32;
|
||||||
var step = new WVec(0, -stepSize, 0)
|
var step = new WVec(0, -stepSize, 0)
|
||||||
.Rotate(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(hFacing))); // Step vector of length 128
|
.Rotate(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(hFacing))); // Step vector of length 128
|
||||||
|
|
||||||
@@ -408,30 +421,94 @@ namespace OpenRA.Mods.Common.Effects
|
|||||||
var posProbe = pos;
|
var posProbe = pos;
|
||||||
var curDist = 0;
|
var curDist = 0;
|
||||||
var tickLimit = System.Math.Min(maxLookaheadDistance, distCheck) / stepSize;
|
var tickLimit = System.Math.Min(maxLookaheadDistance, distCheck) / stepSize;
|
||||||
|
var prevHt = 0;
|
||||||
|
|
||||||
|
// TODO: Make sure cell on map!!!
|
||||||
for (var tick = 0; tick <= tickLimit; tick++)
|
for (var tick = 0; tick <= tickLimit; tick++)
|
||||||
{
|
{
|
||||||
posProbe += step;
|
posProbe += step;
|
||||||
curDist += stepSize;
|
if (!world.Map.Contains(world.Map.CellContaining(posProbe)))
|
||||||
|
break;
|
||||||
|
|
||||||
var ht = world.Map.MapHeight.Value[world.Map.CellContaining(posProbe)] * 512;
|
var ht = world.Map.MapHeight.Value[world.Map.CellContaining(posProbe)] * 512;
|
||||||
|
|
||||||
|
curDist += stepSize;
|
||||||
if (ht > predClfHgt)
|
if (ht > predClfHgt)
|
||||||
{
|
{
|
||||||
predClfHgt = ht;
|
predClfHgt = ht;
|
||||||
predClfDist = curDist;
|
predClfDist = curDist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (prevHt != ht)
|
||||||
|
{
|
||||||
|
lastHtChg = curDist;
|
||||||
|
lastHt = prevHt;
|
||||||
|
prevHt = ht;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int HomingInnerTick(int predClfDist, int diffClfMslHgt, int relTarHorDist,
|
int IncreaseAltitude(int predClfDist, int diffClfMslHgt, int relTarHorDist, int vFacing)
|
||||||
|
{
|
||||||
|
var desiredVFacing = vFacing;
|
||||||
|
|
||||||
|
// If missile is below incline top height and facing downwards, bring back
|
||||||
|
// its vertical facing above zero as soon as possible
|
||||||
|
if ((sbyte)vFacing < 0)
|
||||||
|
desiredVFacing = info.VerticalRateOfTurn;
|
||||||
|
|
||||||
|
// Missile will climb around incline top if bringing vertical facing
|
||||||
|
// down to zero on an arc of radius loopRadius
|
||||||
|
else if (IsNearInclineTop(vFacing, loopRadius, predClfDist)
|
||||||
|
&& WillClimbAroundInclineTop(vFacing, loopRadius, predClfDist, diffClfMslHgt, speed))
|
||||||
|
desiredVFacing = 0;
|
||||||
|
|
||||||
|
// Missile will not climb terrAltDiff w-units within hHeightChange w-units
|
||||||
|
// all the while ending the ascent with vertical facing 0
|
||||||
|
else if (!WillClimbWithinDistance(vFacing, loopRadius, predClfDist, diffClfMslHgt))
|
||||||
|
|
||||||
|
// Find smallest vertical facing, attainable in the next tick,
|
||||||
|
// for which the missile will be able to climb terrAltDiff w-units
|
||||||
|
// within hHeightChange w-units all the while ending the ascent
|
||||||
|
// with vertical facing 0
|
||||||
|
for (var vFac = System.Math.Min(vFacing + info.VerticalRateOfTurn - 1, 63); vFac >= vFacing; vFac--)
|
||||||
|
if (!WillClimbWithinDistance(vFac, loopRadius, predClfDist, diffClfMslHgt)
|
||||||
|
&& !(predClfDist <= loopRadius * (1024 - WAngle.FromFacing(vFac).Sin()) / 1024
|
||||||
|
&& WillClimbAroundInclineTop(vFac, loopRadius, predClfDist, diffClfMslHgt, speed)))
|
||||||
|
{
|
||||||
|
desiredVFacing = vFac + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attained height after ascent as predicted from upper part of incline surmounting manoeuvre
|
||||||
|
var predAttHght = loopRadius * (1024 - WAngle.FromFacing(vFacing).Cos()) / 1024 - diffClfMslHgt;
|
||||||
|
|
||||||
|
// Should the missile be slowed down in order to make it more manoeuverable
|
||||||
|
var slowDown = info.Acceleration.Length != 0 // Possible to decelerate
|
||||||
|
&& ((desiredVFacing != 0 // Lower part of incline surmounting manoeuvre
|
||||||
|
|
||||||
|
// Incline will be hit before vertical facing attains 64
|
||||||
|
&& (predClfDist <= loopRadius * (1024 - WAngle.FromFacing(vFacing).Sin()) / 1024
|
||||||
|
|
||||||
|
// When evaluating this the incline will be *not* be hit before vertical facing attains 64
|
||||||
|
// At current speed target too close to hit without passing it by
|
||||||
|
|| relTarHorDist <= 2 * loopRadius * (2048 - WAngle.FromFacing(vFacing).Sin()) / 1024 - predClfDist))
|
||||||
|
|
||||||
|
|| (desiredVFacing == 0 // Upper part of incline surmounting manoeuvre
|
||||||
|
&& relTarHorDist <= loopRadius * WAngle.FromFacing(vFacing).Sin() / 1024
|
||||||
|
+ Exts.ISqrt(predAttHght * (2 * loopRadius - predAttHght)))); // Target too close to hit at current speed
|
||||||
|
|
||||||
|
if (slowDown)
|
||||||
|
ChangeSpeed(-1);
|
||||||
|
|
||||||
|
return desiredVFacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HomingInnerTick(int predClfDist, int diffClfMslHgt, int relTarHorDist, int lastHtChg, int lastHt,
|
||||||
int nxtRelTarHorDist, int relTarHgt, int vFacing, bool targetPassedBy)
|
int nxtRelTarHorDist, int relTarHgt, int vFacing, bool targetPassedBy)
|
||||||
{
|
{
|
||||||
int desiredVFacing = vFacing;
|
int desiredVFacing = vFacing;
|
||||||
|
|
||||||
// NOTE: Might be desired to unhardcode the distance from target
|
|
||||||
// at which the missile no longer cruises at cruise altitude
|
|
||||||
// but instead keeps trying to hit the target
|
|
||||||
// It still avoids inclines however
|
|
||||||
int targetLockonDistance = 2 * loopRadius;
|
|
||||||
|
|
||||||
// Incline coming up -> attempt to reach the incline so that after predClfDist
|
// Incline coming up -> attempt to reach the incline so that after predClfDist
|
||||||
// the height above the terrain is positive but as close to 0 as possible
|
// the height above the terrain is positive but as close to 0 as possible
|
||||||
// Also, never change horizontal facing and never travel backwards
|
// Also, never change horizontal facing and never travel backwards
|
||||||
@@ -442,96 +519,137 @@ namespace OpenRA.Mods.Common.Effects
|
|||||||
// the missile hasn't been fired near a cliff) is simply finding the smallest
|
// the missile hasn't been fired near a cliff) is simply finding the smallest
|
||||||
// vertical facing that allows for a smooth climb to the new terrain's height
|
// vertical facing that allows for a smooth climb to the new terrain's height
|
||||||
// and coming in at predClfDist at exactly zero vertical facing
|
// and coming in at predClfDist at exactly zero vertical facing
|
||||||
if (diffClfMslHgt >= 0)
|
if (diffClfMslHgt >= 0 && !allowPassBy)
|
||||||
{
|
desiredVFacing = IncreaseAltitude(predClfDist, diffClfMslHgt, relTarHorDist, vFacing);
|
||||||
// If missile is below incline top height and facing downwards, bring back
|
else if (relTarHorDist <= 3 * loopRadius || state == States.Hitting)
|
||||||
// its vertical facing above zero as soon as possible
|
|
||||||
if ((sbyte)vFacing < 0)
|
|
||||||
desiredVFacing = info.VerticalRateOfTurn;
|
|
||||||
|
|
||||||
// Missile will climb around incline top if bringing vertical facing
|
|
||||||
// down to zero on an arc of radius loopRadius
|
|
||||||
else if (IsNearInclineTop(vFacing, loopRadius, predClfDist)
|
|
||||||
&& WillClimbAroundInclineTop(vFacing, loopRadius, predClfDist, diffClfMslHgt, speed))
|
|
||||||
desiredVFacing = 0;
|
|
||||||
|
|
||||||
// Missile will not climb terrAltDiff w-units within hHeightChange w-units
|
|
||||||
// all the while ending the ascent with vertical facing 0
|
|
||||||
else if (!WillClimbWithinDistance(vFacing, loopRadius, predClfDist, diffClfMslHgt))
|
|
||||||
|
|
||||||
// Find smallest vertical facing, attainable in the next tick,
|
|
||||||
// for which the missile will be able to climb terrAltDiff w-units
|
|
||||||
// within hHeightChange w-units all the while ending the ascent
|
|
||||||
// with vertical facing 0
|
|
||||||
for (var vFac = System.Math.Min(vFacing + info.VerticalRateOfTurn - 1, 63); vFac > vFacing; vFac--)
|
|
||||||
if (!WillClimbWithinDistance(vFac, loopRadius, predClfDist, diffClfMslHgt)
|
|
||||||
&& !(predClfDist <= loopRadius * (1024 - WAngle.FromFacing(vFac).Sin()) / 1024
|
|
||||||
&& WillClimbAroundInclineTop(vFac, loopRadius, predClfDist, diffClfMslHgt, speed)))
|
|
||||||
{
|
|
||||||
desiredVFacing = vFac + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attained height after ascent as predicted from upper part of incline surmounting manoeuvre
|
|
||||||
var predAttHght = loopRadius * (1024 - WAngle.FromFacing(vFacing).Cos()) / 1024 - diffClfMslHgt;
|
|
||||||
|
|
||||||
// Should the missile be slowed down in order to make it more manoeuverable
|
|
||||||
var slowDown = info.Acceleration.Length != 0 // Possible to decelerate
|
|
||||||
&& ((desiredVFacing != 0 // Lower part of incline surmounting manoeuvre
|
|
||||||
|
|
||||||
// Incline will be hit before vertical facing attains 64
|
|
||||||
&& (predClfDist <= loopRadius * (1024 - WAngle.FromFacing(vFacing).Sin()) / 1024
|
|
||||||
|
|
||||||
// When evaluating this the incline will be *not* be hit before vertical facing attains 64
|
|
||||||
// At current speed target too close to hit without passing it by
|
|
||||||
|| relTarHorDist <= 2 * loopRadius * (2048 - WAngle.FromFacing(vFacing).Sin()) / 1024 - predClfDist))
|
|
||||||
|
|
||||||
|| (desiredVFacing == 0 // Upper part of incline surmounting manoeuvre
|
|
||||||
&& relTarHorDist <= loopRadius * WAngle.FromFacing(vFacing).Sin() / 1024
|
|
||||||
+ Exts.ISqrt(predAttHght * (2 * loopRadius - predAttHght)))); // Target too close to hit at current speed
|
|
||||||
|
|
||||||
if (slowDown)
|
|
||||||
ChangeSpeed(-1);
|
|
||||||
}
|
|
||||||
else if (nxtRelTarHorDist <= 2 * loopRadius || state == States.Hitting)
|
|
||||||
{
|
{
|
||||||
// No longer travel at cruise altitude
|
// No longer travel at cruise altitude
|
||||||
state = States.Hitting;
|
state = States.Hitting;
|
||||||
|
|
||||||
// Aim for the target
|
if (lastHt >= targetPosition.Z)
|
||||||
var vDist = new WVec(-relTarHgt, -relTarHorDist, 0);
|
allowPassBy = true;
|
||||||
desiredVFacing = (sbyte)OpenRA.Traits.Util.GetFacing(vDist, vFacing);
|
|
||||||
|
|
||||||
// Do not accept -1 as valid vertical facing since it is usually a numerical error
|
if (!allowPassBy && (lastHt < targetPosition.Z || targetPassedBy))
|
||||||
// and will lead to premature descent and crashing into the ground
|
{
|
||||||
if (desiredVFacing == -1)
|
// Aim for the target
|
||||||
desiredVFacing = 0;
|
var vDist = new WVec(-relTarHgt, -relTarHorDist, 0);
|
||||||
|
desiredVFacing = (sbyte)OpenRA.Traits.Util.GetFacing(vDist, vFacing);
|
||||||
|
|
||||||
// If the target has been passed by, limit the absolute value of
|
// Do not accept -1 as valid vertical facing since it is usually a numerical error
|
||||||
// vertical facing by the maximum vertical rate of turn
|
// and will lead to premature descent and crashing into the ground
|
||||||
// Do this because the missile will be looping horizontally
|
if (desiredVFacing == -1)
|
||||||
// and thus needs smaller vertical facings so as not
|
desiredVFacing = 0;
|
||||||
// to hit the ground prematurely
|
|
||||||
if (targetPassedBy)
|
|
||||||
desiredVFacing = desiredVFacing.Clamp(-info.VerticalRateOfTurn, info.VerticalRateOfTurn);
|
|
||||||
else
|
|
||||||
{ // Before the target is passed by, missile speed should be changed
|
|
||||||
// Target's height above loop's center
|
|
||||||
var tarHgt = (loopRadius * WAngle.FromFacing(vFacing).Cos() / 1024 - System.Math.Abs(relTarHgt)).Clamp(0, loopRadius);
|
|
||||||
|
|
||||||
// Target's horizontal distance from loop's center
|
// If the target has been passed by, limit the absolute value of
|
||||||
var tarDist = Exts.ISqrt(loopRadius * loopRadius - tarHgt * tarHgt);
|
// vertical facing by the maximum vertical rate of turn
|
||||||
|
// Do this because the missile will be looping horizontally
|
||||||
|
// and thus needs smaller vertical facings so as not
|
||||||
|
// to hit the ground prematurely
|
||||||
|
if (targetPassedBy)
|
||||||
|
desiredVFacing = desiredVFacing.Clamp(-info.VerticalRateOfTurn, info.VerticalRateOfTurn);
|
||||||
|
else if (lastHt == 0)
|
||||||
|
{ // Before the target is passed by, missile speed should be changed
|
||||||
|
// Target's height above loop's center
|
||||||
|
var tarHgt = (loopRadius * WAngle.FromFacing(vFacing).Cos() / 1024 - System.Math.Abs(relTarHgt)).Clamp(0, loopRadius);
|
||||||
|
|
||||||
// Missile's horizontal distance from loop's center
|
// Target's horizontal distance from loop's center
|
||||||
var missDist = loopRadius * WAngle.FromFacing(vFacing).Sin() / 1024;
|
var tarDist = Exts.ISqrt(loopRadius * loopRadius - tarHgt * tarHgt);
|
||||||
|
|
||||||
// If the current height does not permit the missile
|
// Missile's horizontal distance from loop's center
|
||||||
// to hit the target before passing it by, lower speed
|
var missDist = loopRadius * WAngle.FromFacing(vFacing).Sin() / 1024;
|
||||||
// Otherwise, increase speed
|
|
||||||
if (relTarHorDist <= tarDist - System.Math.Sign(relTarHgt) * missDist)
|
// If the current height does not permit the missile
|
||||||
ChangeSpeed(-1);
|
// to hit the target before passing it by, lower speed
|
||||||
|
// Otherwise, increase speed
|
||||||
|
if (relTarHorDist <= tarDist - System.Math.Sign(relTarHgt) * missDist)
|
||||||
|
ChangeSpeed(-1);
|
||||||
|
else
|
||||||
|
ChangeSpeed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (allowPassBy || (lastHt != 0 && relTarHorDist - lastHtChg < loopRadius))
|
||||||
|
{
|
||||||
|
// Only activate this part if target too close to cliff
|
||||||
|
allowPassBy = true;
|
||||||
|
|
||||||
|
// Vector from missile's current position pointing to the loop's center
|
||||||
|
var radius = new WVec(loopRadius, 0, 0)
|
||||||
|
.Rotate(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(64 - vFacing)));
|
||||||
|
|
||||||
|
// Vector from loop's center to incline top hardcoded in height buffer zone
|
||||||
|
var edgeVector = new WVec(lastHtChg, lastHt - pos.Z, 0) - radius;
|
||||||
|
|
||||||
|
if (!targetPassedBy)
|
||||||
|
{
|
||||||
|
// Climb to critical height
|
||||||
|
if (relTarHorDist > 2 * loopRadius)
|
||||||
|
{
|
||||||
|
// Target's distance from cliff
|
||||||
|
var d1 = relTarHorDist - lastHtChg;
|
||||||
|
if (d1 < 0)
|
||||||
|
d1 = 0;
|
||||||
|
if (d1 > 2 * loopRadius)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Find critical height at which the missile must be once it is at one loopRadius
|
||||||
|
// away from the target
|
||||||
|
var h1 = loopRadius - Exts.ISqrt(d1 * (2 * loopRadius - d1)) - (pos.Z - lastHt);
|
||||||
|
|
||||||
|
if (h1 > loopRadius * (1024 - WAngle.FromFacing(vFacing).Cos()) / 1024)
|
||||||
|
desiredVFacing = WAngle.ArcTan(Exts.ISqrt(h1 * (2 * loopRadius - h1)), loopRadius - h1).Angle >> 2;
|
||||||
|
else
|
||||||
|
desiredVFacing = 0;
|
||||||
|
|
||||||
|
// TODO: deceleration checks!!!
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Avoid the cliff edge
|
||||||
|
if (edgeVector.Length > loopRadius && lastHt > targetPosition.Z)
|
||||||
|
{
|
||||||
|
int vFac;
|
||||||
|
for (vFac = vFacing + 1; vFac <= vFacing + info.VerticalRateOfTurn - 1; vFac++)
|
||||||
|
{
|
||||||
|
// Vector from missile's current position pointing to the loop's center
|
||||||
|
radius = new WVec(loopRadius, 0, 0)
|
||||||
|
.Rotate(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(64 - vFac)));
|
||||||
|
|
||||||
|
// Vector from loop's center to incline top + 64 hardcoded in height buffer zone
|
||||||
|
edgeVector = new WVec(lastHtChg, lastHt - pos.Z, 0) - radius;
|
||||||
|
if (edgeVector.Length <= loopRadius)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
desiredVFacing = vFac;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Aim for the target
|
||||||
|
var vDist = new WVec(-relTarHgt, -relTarHorDist * (targetPassedBy ? -1 : 1), 0);
|
||||||
|
desiredVFacing = (sbyte)OpenRA.Traits.Util.GetFacing(vDist, vFacing);
|
||||||
|
if (desiredVFacing < 0 && info.VerticalRateOfTurn < (sbyte)vFacing)
|
||||||
|
desiredVFacing = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
ChangeSpeed();
|
{
|
||||||
|
// Aim for the target
|
||||||
|
var vDist = new WVec(-relTarHgt, -relTarHorDist * (targetPassedBy ? -1 : 1), 0);
|
||||||
|
desiredVFacing = (sbyte)OpenRA.Traits.Util.GetFacing(vDist, vFacing);
|
||||||
|
if (desiredVFacing < 0 && info.VerticalRateOfTurn < (sbyte)vFacing)
|
||||||
|
desiredVFacing = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Aim to attain cruise altitude as soon as possible while having the absolute value
|
||||||
|
// of vertical facing bound by the maximum vertical rate of turn
|
||||||
|
var vDist = new WVec(-diffClfMslHgt - info.CruiseAltitude.Length, -speed, 0);
|
||||||
|
desiredVFacing = (sbyte)OpenRA.Traits.Util.GetFacing(vDist, vFacing);
|
||||||
|
desiredVFacing = desiredVFacing.Clamp(-info.VerticalRateOfTurn, info.VerticalRateOfTurn);
|
||||||
|
|
||||||
|
ChangeSpeed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -550,9 +668,8 @@ namespace OpenRA.Mods.Common.Effects
|
|||||||
|
|
||||||
WVec HomingTick(World world, WVec tarDistVec, int relTarHorDist)
|
WVec HomingTick(World world, WVec tarDistVec, int relTarHorDist)
|
||||||
{
|
{
|
||||||
int predClfHgt;
|
int predClfHgt, predClfDist, lastHtChg, lastHt;
|
||||||
int predClfDist;
|
InclineLookahead(world, relTarHorDist, out predClfHgt, out predClfDist, out lastHtChg, out lastHt);
|
||||||
InclineLookahead(world, relTarHorDist, out predClfHgt, out predClfDist);
|
|
||||||
|
|
||||||
// Height difference between the incline height and missile height
|
// Height difference between the incline height and missile height
|
||||||
var diffClfMslHgt = predClfHgt - pos.Z;
|
var diffClfMslHgt = predClfHgt - pos.Z;
|
||||||
@@ -565,10 +682,20 @@ namespace OpenRA.Mods.Common.Effects
|
|||||||
|
|
||||||
// Compute which direction the projectile should be facing
|
// Compute which direction the projectile should be facing
|
||||||
var desiredHFacing = OpenRA.Traits.Util.GetFacing(tarDistVec + predVel, hFacing);
|
var desiredHFacing = OpenRA.Traits.Util.GetFacing(tarDistVec + predVel, hFacing);
|
||||||
var desiredVFacing = HomingInnerTick(predClfDist, diffClfMslHgt, relTarHorDist, nxtRelTarHorDist, relTarHgt, vFacing, targetPassedBy);
|
|
||||||
|
|
||||||
// The target will be passed by at the end of the tick
|
if (allowPassBy && System.Math.Abs(desiredHFacing - hFacing) >= System.Math.Abs(desiredHFacing + 128 - hFacing))
|
||||||
if (nxtRelTarHorDist == 0)
|
{
|
||||||
|
desiredHFacing += 128;
|
||||||
|
targetPassedBy = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
targetPassedBy = false;
|
||||||
|
|
||||||
|
var desiredVFacing = HomingInnerTick(predClfDist, diffClfMslHgt, relTarHorDist, lastHtChg, lastHt,
|
||||||
|
nxtRelTarHorDist, relTarHgt, vFacing, targetPassedBy);
|
||||||
|
|
||||||
|
// The target has been passed by
|
||||||
|
if (tarDistVec.HorizontalLength < speed * WAngle.FromFacing(vFacing).Cos() / 1024)
|
||||||
targetPassedBy = true;
|
targetPassedBy = true;
|
||||||
|
|
||||||
// Check whether the homing mechanism is jammed
|
// Check whether the homing mechanism is jammed
|
||||||
|
|||||||
Reference in New Issue
Block a user