// Finds another point near the destination. Useful when toon is 'waiting' for something // (e.g., boat, mob repops, etc). This allows multiple people running // the same profile to not stand on top of each other while waiting for // something. public static WoWPoint FanOutRandom(this WoWPoint location, double maxRadius) { const int CYLINDER_LINE_COUNT = 12; const int MAX_TRIES = 50; const double SAFE_DISTANCE_BUFFER = 1.75; WoWPoint candidateDestination = location; int tryCount; // Most of the time we'll find a viable spot in less than 2 tries... // However, if you're standing on a pier, or small platform a // viable alternative may take 10-15 tries--its all up to the // random number generator. for (tryCount = MAX_TRIES; tryCount > 0; --tryCount) { WoWPoint circlePoint; bool[] hitResults; WoWPoint[] hitPoints; int index; WorldLine[] traceLines = new WorldLine[CYLINDER_LINE_COUNT +1]; candidateDestination = location.AddPolarXY((TAU * _random.NextDouble()), (maxRadius * _random.NextDouble()), 0.0); // Build set of tracelines that can evaluate the candidate destination -- // We build a cone of lines with the cone's base at the destination's 'feet', // and the cone's point at maxRadius over the destination's 'head'. We also // include the cone 'normal' as the first entry. // 'Normal' vector index = 0; traceLines[index].Start = candidateDestination.Add(0.0, 0.0, maxRadius); traceLines[index].End = candidateDestination.Add(0.0, 0.0, -maxRadius); // Cylinder vectors for (double turnFraction = 0.0; turnFraction < TAU; turnFraction += (TAU / CYLINDER_LINE_COUNT)) { ++index; circlePoint = candidateDestination.AddPolarXY(turnFraction, SAFE_DISTANCE_BUFFER, 0.0); traceLines[index].Start = circlePoint.Add(0.0, 0.0, maxRadius); traceLines[index].End = circlePoint.Add(0.0, 0.0, -maxRadius); } // Evaluate the cylinder... // The result for the 'normal' vector (first one) will be the location where the // destination meets the ground. Before this MassTrace, only the candidateDestination's // X/Y values were valid. GameWorld.MassTraceLine(traceLines.ToArray(), GameWorld.CGWorldFrameHitFlags.HitTestGroundAndStructures, out hitResults, out hitPoints); candidateDestination = hitPoints[0]; // From 'normal', Destination with valid Z coordinate // Sanity check... // We don't want to be standing right on the edge of a drop-off (say we'e on // a plaform or pier). If there is not solid ground all around us, we reject // the candidate. Our test for validity is that the walking distance must // not be more than 20% greater than the straight-line distance to the point. int viableVectorCount = hitPoints.Sum(point => ((Me.Location.SurfacePathDistance(point) < (Me.Location.Distance(point) * 1.20)) ? 1 : 0)); if (viableVectorCount < (CYLINDER_LINE_COUNT +1)) { continue; } // If new destination is 'too close' to our current position, try again... if (Me.Location.Distance(candidateDestination) <= SAFE_DISTANCE_BUFFER) { continue; } break; } // If we exhausted our tries, just go with simple destination -- if (tryCount <= 0) { candidateDestination = location; } return (candidateDestination); }
// Finds another point near the destination. Useful when toon is 'waiting' for something // (e.g., boat, mob repops, etc). This allows multiple people running // the same profile to not stand on top of each other while waiting for // something. public static WoWPoint FanOutRandom(this WoWPoint location, double maxRadius) { const int CYLINDER_LINE_COUNT = 12; const int MAX_TRIES = 50; const double SAFE_DISTANCE_BUFFER = 1.75; WoWPoint candidateDestination = location; int tryCount; // Most of the time we'll find a viable spot in less than 2 tries... // However, if you're standing on a pier, or small platform a // viable alternative may take 10-15 tries--its all up to the // random number generator. for (tryCount = MAX_TRIES; tryCount > 0; --tryCount) { WoWPoint circlePoint; bool[] hitResults; WoWPoint[] hitPoints; int index; WorldLine[] traceLines = new WorldLine[CYLINDER_LINE_COUNT + 1]; candidateDestination = location.AddPolarXY((TAU * StyxWoW.Random.NextDouble()), (maxRadius * StyxWoW.Random.NextDouble()), 0.0); // Build set of tracelines that can evaluate the candidate destination -- // We build a cone of lines with the cone's base at the destination's 'feet', // and the cone's point at maxRadius over the destination's 'head'. We also // include the cone 'normal' as the first entry. // 'Normal' vector index = 0; traceLines[index].Start = candidateDestination.Add(0.0, 0.0, maxRadius); traceLines[index].End = candidateDestination.Add(0.0, 0.0, -maxRadius); // Cylinder vectors for (double turnFraction = 0.0; turnFraction < TAU; turnFraction += (TAU / CYLINDER_LINE_COUNT)) { ++index; circlePoint = candidateDestination.AddPolarXY(turnFraction, SAFE_DISTANCE_BUFFER, 0.0); traceLines[index].Start = circlePoint.Add(0.0, 0.0, maxRadius); traceLines[index].End = circlePoint.Add(0.0, 0.0, -maxRadius); } // Evaluate the cylinder... // The result for the 'normal' vector (first one) will be the location where the // destination meets the ground. Before this MassTrace, only the candidateDestination's // X/Y values were valid. GameWorld.MassTraceLine(traceLines.ToArray(), TraceLineHitFlags.Collision, out hitResults, out hitPoints); candidateDestination = hitPoints[0]; // From 'normal', Destination with valid Z coordinate // Sanity check... // We don't want to be standing right on the edge of a drop-off (say we'e on // a plaform or pier). If there is not solid ground all around us, we reject // the candidate. Our test for validity is that the walking distance must // not be more than 20% greater than the straight-line distance to the point. int viableVectorCount = hitPoints.Sum(point => ((Me.Location.SurfacePathDistance(point) < (Me.Location.Distance(point) * 1.20)) ? 1 : 0)); if (viableVectorCount < (CYLINDER_LINE_COUNT + 1)) { continue; } // If new destination is 'too close' to our current position, try again... if (Me.Location.Distance(candidateDestination) <= SAFE_DISTANCE_BUFFER) { continue; } break; } // If we exhausted our tries, just go with simple destination -- if (tryCount <= 0) { candidateDestination = location; } return(candidateDestination); }
/// <summary> /// <para>Finds another point near the destination. Useful when toon is 'waiting' for something /// (e.g., boat, mob repops, etc). This allows multiple people running /// the same profile to not stand on top of each other while waiting for /// something.</para> /// <para>Notes:<list type="bullet"> /// <item><description><para> * The returned Vector3 is carefully chosen. The returned Vector3 /// will not cause you to fall off a boat dock or Zeppelin landing.</para></description></item> /// </list></para> /// </summary> /// <param name="location"></param> /// <param name="maxRadius"></param> /// <returns></returns> /// <remarks>17Apr2011-12:16UTC chinajade</remarks> public static Vector3 FanOutRandom(this Vector3 location, double maxRadius) { Contract.Requires(maxRadius >= 0.0, context => "maxRadius >= 0.0"); // Optimize situations where we want a very close-by point... if (maxRadius <= 1) { return(location); } const int CYLINDER_LINE_COUNT = 12; const int MAX_TRIES = 50; const double SAFE_DISTANCE_BUFFER = 1.75; Vector3 candidateDestination = location; int tryCount; // ActiveMover is null in some cases where player is not in control of movement, // such as when on a taxi like the one for the // 'Mission: The Murketh and Shaadraz Gateways' quest (http://www.wowhead.com/quest=10146) var me = WoWMovement.ActiveMover ?? StyxWoW.Me; Contract.Requires(me != null, context => "me != null"); var myLoc = me.Location; // Most of the time we'll find a viable spot in less than 2 tries... // However, if you're standing on a pier, or small platform a // viable alternative may take 10-15 tries--its all up to the // random number generator. for (tryCount = MAX_TRIES; tryCount > 0; --tryCount) { bool[] hitResults; Vector3[] hitPoints; Func <double, double> weightedRandomRadius = (radiusMaximum) => { return ((StyxWoW.Random.Next(101) < 80) // We want a large number of the candidate magnitudes to be near the max range. // This encourages toons to 'spread out'. ? ((radiusMaximum * 0.70) + (radiusMaximum * 0.30 * StyxWoW.Random.NextDouble())) : (radiusMaximum * StyxWoW.Random.NextDouble())); }; var traceLines = new WorldLine[CYLINDER_LINE_COUNT + 1]; candidateDestination = location.AddPolarXY((TAU * StyxWoW.Random.NextDouble()), weightedRandomRadius(maxRadius), 0.0); // If destination is in the air... if (!IsOverGround(candidateDestination, 3.0)) { // If we don't have clear LoS between the specified and candidate destinations, the candidate is unsuitable... if (GameWorld.TraceLine(location, candidateDestination, TraceLineHitFlags.Collision)) { continue; } // Otherwise, we have our candidate destination... break; } // Ground-based destinations... // Build set of tracelines that can evaluate the candidate destination -- // We build a cone of lines with the cone's base at the destination's 'feet', // and the cone's point at maxRadius over the destination's 'head'. We also // include the cone 'normal' as the first entry. // 'Normal' vector var index = 0; traceLines[index].Start = candidateDestination.Add(0.0, 0.0, maxRadius); traceLines[index].End = candidateDestination.Add(0.0, 0.0, -maxRadius); // Cylinder vectors for (double turnFraction = 0.0; turnFraction < TAU; turnFraction += (TAU / CYLINDER_LINE_COUNT)) { ++index; var circlePoint = candidateDestination.AddPolarXY(turnFraction, SAFE_DISTANCE_BUFFER, 0.0); traceLines[index].Start = circlePoint.Add(0.0, 0.0, maxRadius); traceLines[index].End = circlePoint.Add(0.0, 0.0, -maxRadius); } // Evaluate the cylinder... // The result for the 'normal' vector (first one) will be the location where the // destination meets the ground. Before this MassTrace, only the candidateDestination's // X/Y values were valid. GameWorld.MassTraceLine(traceLines.ToArray(), TraceLineHitFlags.Collision, out hitResults, out hitPoints); candidateDestination = hitPoints[0]; // From 'normal', Destination with valid Z coordinate // Sanity check... // We don't want to be standing right on the edge of a drop-off (say we'e on // a plaform or pier). If there is not solid ground all around us, we reject // the candidate. Our test for validity is that the walking distance must // not be more than 20% greater than the straight-line distance to the point. // TODO: FanOutRandom PathTraversalCost on point (replace with HB's SampleMesh method instead) int viableVectorCount = hitPoints.Sum(point => ((location./*PathTraversalCost*/ Distance(point) < (location.Distance(point) * 1.20)) ? 1 : 0)); if (viableVectorCount < (CYLINDER_LINE_COUNT * 0.8)) { continue; } // If new destination is 'too close' to our current position, try again... if (myLoc.Distance(candidateDestination) <= SAFE_DISTANCE_BUFFER) { continue; } break; } // If we exhausted our tries, just go with simple destination -- if (tryCount <= 0) { candidateDestination = location; } return(candidateDestination); }