// **********************************************************************************************
// ***** Special Zig-Zag movement for whirlwind/tempest *****
// **********************************************************************************************
public static Vector3 FindZigZagTargetLocation(Vector3 vTargetLocation, float fDistanceOutreach, bool bRandomizeDistance = false, bool bNinetyDegree = false)
{
Vector3 vThisZigZag = vNullLocation;
Random rndNum = new Random(int.Parse(Guid.NewGuid().ToString().Substring(0, 8), NumberStyles.HexNumber));
float iFakeStart = 0;
if (bRandomizeDistance)
fDistanceOutreach += rndNum.Next(18);
float fDirectionToTarget = FindDirectionDegree(playerStatus.vCurrentPosition, vTargetLocation);
float fPointToTarget;
float fHighestWeight = float.NegativeInfinity;
Vector3 vBestLocation = vNullLocation;
bool bFoundSafeSpotsFirstLoop = false;
float fAdditionalRange = 0f;
//K: Direction is more important than distance
//K: 0-15 and 0-(-15) degree is the best for point to target, and Z will hardly change if distance is only 20f
// 15-60 and -15-(-60) degree can still hit the target, but we may miss. and Z is nearly between currentposition and target position according to math(...)
// 60-90 and -60-(-90) degree is for avoid obstacles, because we cannot get close to target with WW. but the Z cannot be estimated if the distance is too far
// we can only cast to nearby with the same Z position and shorter distance.
for (int iMultiplier = 1; iMultiplier <= 2; iMultiplier++)
{
if (iMultiplier == 2)
{
if (bFoundSafeSpotsFirstLoop)
break;
fAdditionalRange = 150f;
iFakeStart = (rndNum.Next(12) * 5);
}
float fRunDistance = fDistanceOutreach;
for (float iDegreeChange = iFakeStart; iDegreeChange <= 30f+fAdditionalRange; iDegreeChange += 5)
{
float iPosition = iDegreeChange;
//point to target is better, otherwise we have to avoid obstacle first
if (iPosition > 105f)
iPosition = 90f-iPosition;
else if (iPosition > 30f)
iPosition -= 15f;
else if (iPosition > 15f)
iPosition = 15f-iPosition;
fPointToTarget = iPosition;
iPosition += fDirectionToTarget;
if (iPosition < 0)
iPosition = 360 + iPosition;
if (iPosition > 360)
iPosition = iPosition - 360;
vThisZigZag = MathEx.GetPointAt(playerStatus.vCurrentPosition, fRunDistance, MathEx.ToRadians(iPosition));
if (fPointToTarget <= 30f || fPointToTarget >= 330f)
{
vThisZigZag.Z = vTargetLocation.Z;
}
else if (fPointToTarget <= 60f || fPointToTarget >= 300f)
{
//K: we are trying to find position that we can circle around the target
// but we shouldn't run too far away from target
vThisZigZag.Z = (vTargetLocation.Z + playerStatus.vCurrentPosition.Z) / 2;
fRunDistance = fDistanceOutreach - 5f;
}
else
{
//K: don't move too far if we are not point to target, we just try to move
// this can help a lot when we are near stairs
fRunDistance = 5f;
}
// Give weight to each zigzag point, so we can find the best one to aim for
if (ZetaDia.Physics.Raycast(playerStatus.vCurrentPosition, vThisZigZag, NavCellFlags.AllowWalk))
{
//Log("Find a right location" + iPosition);
bool bAnyAvoidance = false;
float fThisWeight = 1000f;
if (iMultiplier == 2)
fThisWeight -= 80f;
// Remove weight for each avoidance *IN* that location
foreach (GilesObstacle tempobstacle in hashAvoidanceObstacleCache.Where(cp => GilesIntersectsPath(cp.vThisLocation, cp.fThisRadius, playerStatus.vCurrentPosition, vThisZigZag)))
{
bAnyAvoidance = true;
fThisWeight -= (float)tempobstacle.dThisWeight;
}
// Give extra weight to areas we've been inside before
bool bExtraSafetyWeight = hashSkipAheadAreaCache.Any(cp => cp.vThisLocation.Distance(vThisZigZag) <= cp.fThisRadius);
if (bExtraSafetyWeight)
fThisWeight += 100f;
// Use this one if it's more weight, or we haven't even found one yet, or if same weight as another with a random chance
if (fThisWeight > fHighestWeight)
{
fHighestWeight = fThisWeight;
vBestLocation = vThisZigZag;
if (!bAnyAvoidance)
bFoundSafeSpotsFirstLoop = true;
}
} // Can we raycast to the point at minimum?
} // Loop through degrees
} // Loop through multiplier
return vBestLocation;
}