What's new
  • Visit Rebornbuddy
  • Visit Panda Profiles
  • Visit LLamamMagic
  • Visit Resources
  • Visit Downloads
  • Visit Portal

Starting from X and Y coords, determine if you can land on that point

jim87

New Member
Joined
Aug 26, 2011
Messages
445
Reaction score
7
Hello!

I've been looking for this by a while now. I'd like to know how to:

1. calculate a valid "ground-level" WoWPoint starting from X and Y coords (no Z, should take the highest)
2. Validate the WoWPoint relative to the ground meshes, so that I know it's terrain and, for example, not lava nor water
3. Calculate the tangent to know if the point is actually a valid one to land (0 < x < K ok, K <= x < +00 not ok)

Is it possible to do this, or if not, how could I calculate it?

Also, how can I check whenever I can see another WoWPoint from that one I've found?

Thanks!
 
Ok, simplier question:

1. how can I get the highest ground Z coord starting from X and Y?
2. how can I get the ground type of a WoWPoint?

Thanks
 
Ok, I found out a method which reveals the altitude(s) of a X/Y pair coordinate, now I only need to know what kind of material it is... any hint?
 
Ok, I found out a method which reveals the altitude(s) of a X/Y pair coordinate, now I only need to know what kind of material it is... any hint?

To my knowledge, the HB API provides no way to ascertain what you seek.

In fact, there is no way to even determine simple things like is the WoWPoint 'indoors' or 'outdoors' (unless its the WoWPoint at which your toon stands).

I believe the information is available if your willing to cr4ck open DBC files and parse them (ugh! ugh! ugh!). This kind of information you would find on a site like MMOwned, but not here.

I could easily be wrong, about this, but don't think so. Might want to wait for a seasoned (HBcore or Community) developer to confirm or deny this.


cheers,
chinajade
 
Actually fishing bots can determine where to land to fish, so I assume they have some checks. Looking at Highvoltz's code I don't understand what his methods do, because the methods he use in his owns are not documented at all in HB2, like TraceLine, MassTraceLine, RayCast, type "WorldLine", also I loose the logic somewhere in the code, as there are no comments. BTW this is his code:
PHP:
        bool FindPoolPoint(WoWGameObject pool)
        {
            int traceStep = AutoAngler.Instance.MySettings.TraceStep;
            float _PIx2 = 3.14159f * 2f;
            WoWPoint playerLoc = _me.Location;
            WoWPoint p = new WoWPoint();
            WoWPoint hPoint = new WoWPoint();
            WoWPoint lPoint = new WoWPoint();
            WorldLine[] traceLine = new WorldLine[traceStep];
            PoolPoints.Clear();

            // scans starting at 15 yards from player for water at every 18 degress 
            bool[] tracelineRetVals;

            float range = 15;
            int min = AutoAngler.Instance.MySettings.MinPoolRange;
            int max = AutoAngler.Instance.MySettings.MaxPoolRange;
            float step = AutoAngler.Instance.MySettings.PoolRangeStep;
            float delta = step;
            float avg = (min + max) / 2;
            while (true)
            {
                for (int i = 0; i < traceStep; i++)
                {
                    p = pool.Location.RayCast((i * _PIx2) / traceStep, range);
                    hPoint = p; hPoint.Z += 45; lPoint = p; lPoint.Z -= 1;
                    traceLine[i].Start = hPoint;
                    traceLine[i].End = lPoint;
                }
                WoWPoint[] hitPoints;
                GameWorld.MassTraceLine(traceLine, GameWorld.CGWorldFrameHitFlags.HitTestGroundAndStructures,
                    out tracelineRetVals, out hitPoints);
                // what I'm doing here is compare the elevation of 4 corners around a point with 
                // that point's elevation to determine if that point is too steep to stand on.
                List<WorldLine> slopetraces = new List<WorldLine>();
                List<WoWPoint> testPoints = new List<WoWPoint>();
                for (int i = 0; i < traceStep; i++)
                {
                    if (tracelineRetVals[i])
                    {
                        slopetraces.AddRange(GetQuadSloopTraceLines(hitPoints[i]));
                        testPoints.Add(hitPoints[i]);
                    }
                    else if (WaterWalking.CanCast)
                    {
                        traceLine[i].End.Z = pool.Z + 1;
                        PoolPoints.Add(traceLine[i].End);
                    }
                }
                // fire tracelines.. 
                bool[] slopelinesRetVals, lavaRetVals = null;
                WoWPoint[] slopeHits;
                using (new FrameLock())
                {
                    GameWorld.MassTraceLine(slopetraces.ToArray(), GameWorld.CGWorldFrameHitFlags.HitTestGroundAndStructures,
                    out slopelinesRetVals, out slopeHits);
                    if (AutoAngler.Instance.MySettings.AvoidLava)
                    {
                        GameWorld.MassTraceLine(slopetraces.ToArray(), GameWorld.CGWorldFrameHitFlags.HitTestLiquid2,
                         out lavaRetVals);
                    }
                }

                // process results
                PoolPoints.AddRange(ProcessSlopeAndLavaResults(testPoints, slopeHits, lavaRetVals));
                // perform LOS checks
                if (PoolPoints.Count > 0)
                {
                    WorldLine[] losLine = new WorldLine[PoolPoints.Count];
                    for (int i2 = 0; i2 < PoolPoints.Count; i2++)
                    {
                        WoWPoint point = PoolPoints[i2];
                        point.Z += 2;
                        losLine[i2].Start = point;
                        losLine[i2].End = pool.Location;
                    }
                    GameWorld.MassTraceLine(losLine, GameWorld.CGWorldFrameHitFlags.HitTestGroundAndStructures,
                        out tracelineRetVals);
                    for (int i2 = PoolPoints.Count - 1; i2 >= 0; i2--)
                    {
                        if (tracelineRetVals[i2])
                            PoolPoints.RemoveAt(i2);
                    }
                }
                // sort pools by distance to player                
                PoolPoints.Sort((p1, p2) => p1.Distance(_me.Location).CompareTo(p2.Distance(_me.Location)));
                if (!_me.IsFlying)
                {
                    // if we are not flying check if we can genorate a path to points.
                    for (int i = 0; i < PoolPoints.Count; )
                    {
                        WoWPoint[] testP = Navigator.GeneratePath(_me.Location, PoolPoints[i]);
                        if (testP.Length > 0)
                        {
                            return true;
                        }
                        else
                        {
                            PoolPoints.RemoveAt(i);
                            PoolPoints.Sort((a, b) => a.Distance(_me.Location).CompareTo(b.Distance(_me.Location)));
                        }
                    }
                }
                if (PoolPoints.Count > 0)
                    return true;
                bool minCaped = (15 - delta) < min;
                bool maxCaped = (15 + delta) > max;
                if (minCaped && maxCaped)
                    break;

                if ((range <= 15 && (15 + delta) <= max) || minCaped)
                {
                    range = 15 + delta;
                    if (avg < 15 || minCaped)
                        delta += step;
                    continue;
                }

                if ((range > 15 && (15 - delta) >= min) || maxCaped)
                {
                    range = 15 - delta;
                    if (avg >= 15 || maxCaped)
                        delta += step;
                    continue;
                }
            }
            return false;
        }

        static public WorldLine GetSlopeTraceLine(WoWPoint point, float xDelta, float yDelta)
        {
            WoWPoint topP = point;
            topP.X += xDelta;
            topP.Y += yDelta;
            topP.Z += 6;
            WoWPoint botP = topP;
            botP.Z -= 12;
            return new WorldLine(topP, botP);
        }

        static public List<WorldLine> GetQuadSloopTraceLines(WoWPoint point)
        {
            //float delta = AutoAngler2.Instance.MySettings.LandingSpotWidth / 2;
            float delta = 0.5f;
            List<WorldLine> wl = new List<WorldLine>();
            // north west
            wl.Add(GetSlopeTraceLine(point, delta, -delta));
            // north east
            wl.Add(GetSlopeTraceLine(point, delta, delta));
            // south east
            wl.Add(GetSlopeTraceLine(point, -delta, delta));
            // south west
            wl.Add(GetSlopeTraceLine(point, -delta, -delta));
            return wl;
        }

        static public List<WoWPoint> ProcessSlopeAndLavaResults(List<WoWPoint> testPoints, WoWPoint[] slopePoints,
            bool[] lavaHits)
        {
            //float slopeRise = AutoAngler2.Instance.MySettings.LandingSpotSlope / 2;
            float slopeRise = 0.60f;
            List<WoWPoint> retList = new List<WoWPoint>();
            for (int i = 0; i < testPoints.Count; i++)
            {
                if (slopePoints[i * 4] != WoWPoint.Zero &&
                    slopePoints[i * 4 + 1] != WoWPoint.Zero &&
                    slopePoints[i * 4 + 2] != WoWPoint.Zero &&
                    slopePoints[i * 4 + 3] != WoWPoint.Zero &&
                    // check for lava hits
                    (lavaHits == null ||
                    (lavaHits != null &&
                    !lavaHits[i * 4] &&
                    !lavaHits[i * 4 + 1] &&
                    !lavaHits[i * 4 + 2] &&
                    !lavaHits[i * 4 + 3]))
                    )
                {
                    if (ElevationDifference(testPoints[i], slopePoints[(i * 4)]) <= slopeRise &&
                        ElevationDifference(testPoints[i], slopePoints[(i * 4) + 1]) <= slopeRise &&
                        ElevationDifference(testPoints[i], slopePoints[(i * 4) + 2]) <= slopeRise &&
                        ElevationDifference(testPoints[i], slopePoints[(i * 4) + 3]) <= slopeRise)
                    {
                        retList.Add(testPoints[i]);
                    }
                }
            }
            return retList;
        }

        static public float ElevationDifference(WoWPoint p1, WoWPoint p2)
        {
            if (p1.Z > p2.Z)
                return p1.Z - p2.Z;
            else
                return p2.Z - p1.Z;
        }
 
Actually fishing bots can determine where to land to fish, so I assume they have some checks. Looking at Highvoltz's code I don't understand what his methods do, because the methods he use in his owns are not documented at all in HB2, like TraceLine, MassTraceLine, RayCast, type "WorldLine", also I loose the logic somewhere in the code, as there are no comments. BTW this is his code:

Traceline/Raycast were originally 'graphics' concepts. They are CPU-intensive, and only effectively usable for short distances (say, < 100 yards).

The concept of a Traceline/Raycast is it draws a line between PointA and PointB, and is looking to 'hit' something. If nothing is hit, the path is clear. If something is hit, then the location that the line intersected the object is returned. Typically, they are used to detect the location of the ground, a building in front of you, or the water's surface.

MassTraceLine is just a convenience method that does multiple TraceLines in one call, and returns to you an array of intersection points (one for each line in the call).

In the snippet of code you posted, TraceLine is being used for several things. Probably, the most important is "can the toon see the pool from the chosen landing spot". Similarly, Flightor uses TraceLine to determine is it about to hit a mountain, or a collidable tree. The CollectThings behavior uses TraceLine to detect the water's surface.

TraceLine, through its CGWorldFrameHitFlags argument, can tell you whether you're hitting a mobs bounding box (HtTestBoundingModels), the ground (HitTestGround), ground or buildings (HitTestGroundAndStructures), liquids (HitTestLiquid, HitTestLiquid2), line-of-site (HitTestLOS), moveable objects (HitTestMoveableObjects), or 'something else' (HitTestUnknown). Water detection is HitTestLiquid. From Highvoltz code, it looks like lava is detected with HitTestLiquid2. That felfire green liquid in Shadowmoon Valley is probably HitTestLiquid2, also.

With Traceline, you can probably do what you need. Just be aware of its expense, and don't try to use it with line lengths over 100 yards or so.


cheers,
chinajade
 
Last edited:
Thanks chinajade!

I've made this first attempt, but from my tests it results way too heavy to work (several seconds (around 10!) instead of milliseconds), and it not always return me a WoWPoint (i.e. no point found for pool) (when it founds it, it works as expected but the LOS I haven't implemented yet). May you, or any other member of the forum, help me optimizing it, or give me hints where to do things differently?

Thanks!

PHP:
        // Main method, this will call all the others
        public static WoWPoint getNearestLandingPointFromPool(WoWObject pool)
        {
            /* Check all around the pool in circles, 20? by 20?,
             * searching for a valid point where to cast
             * Fishing, starting from 10 up to 20 yards.
             * 
             * Return the nearest WoWPoint if found, (0, 0, 0) otherwise.
             */

            float radians = WoWMathHelper.DegreesToRadians(20.0F);
            int totalChecksPerRound = (int)(2 * Math.PI / radians);
            LocalPlayer Me = ObjectManager.Me;
            WoWPoint candidate = new WoWPoint();

            // Circles cycle
            // from 10 to 20 yards, step = 1 yard
            for (int range = 10; range <= 20; range++)
            {
                // segments cycle, [0; 2PI)
                for (int i = 0; i < totalChecksPerRound; i++)
                {
                    candidate = pool.Location.RayCast(radians * i, range);
                    // Find the most suitable height the point can have (if any)
                    float? height =  findNearestPointHeight(candidate, pool.Location.Z);
                    // No height found
                    if(height == null) continue;

                    candidate.Z = height.Value;

                    // Check if there is ground under the candidate,
                    // if it is a valid point where to cast Fishing
                    // and it's not too far
                    if (checkGroundUnderWoWPoint(candidate)
                        && checkWoWPointStaticStay(candidate)
                        && pool.Location.Distance(candidate) <= 20.0F)
                        return candidate;
                }
            }

            DebugLog("No landing candidate found for pool at {0}, {1}", pool.Location.X, pool.Location.Y);

            // No valid candidates found
            return new WoWPoint(0, 0, 0);
        }

        public static bool checkGroundUnderWoWPoint(WoWPoint point)
        {
            // Create two points, ahead and under
            // the one we're going to test
            WoWPoint top, bottom;
            top = bottom = point;
            top.Z += 2.0F;
            bottom.Z -= 2.0F;

            // Create a line which connects the two new points
            WorldLine line = new WorldLine(top, bottom);

            // Check whenever the line intersects ground or a structure
            bool groundOrLevel = GameWorld.TraceLine(top, bottom, GameWorld.CGWorldFrameHitFlags.HitTestGroundAndStructures);
            // Check whenever the line doesn't cross lava (we don't want to be cooked!)
            bool lava = GameWorld.TraceLine(top, bottom, GameWorld.CGWorldFrameHitFlags.HitTestLiquid2);

            // True if there is a collision with a structure or ground
            // and not with Liquid2 (lava). False otherwise
            return groundOrLevel && !lava;
        }

        public static float? findNearestPointHeight(WoWPoint point, float minHeight) { return findNearestPointHeight(point, minHeight, Single.MaxValue); }
        public static float? findNearestPointHeight(WoWPoint point, float minHeight, float maxDiff)
        {
            List<float> heights = Navigator.FindHeights(point.X, point.Y);
            // No heights found, return null as "not found"
            if (heights.Count == 0)
                return null;
            // Only one height found, return it
            if (heights.Count == 1)
                return heights.First();
            float candidate = Single.MaxValue;
            // Multiple heights, take the nearest from the minHeight
            foreach (float height in heights)
            {
                float diff = height - minHeight;
                // height is higher than minHeight, the difference
                // is lower than the previous candidate and the diff
                // is not out of bounds
                if (diff <= maxDiff && diff > 0 && diff < (candidate - minHeight))
                    candidate = height;
            }
            // no candidate found
            if (candidate == Single.MaxValue)
                return null;
            return candidate;

        }

        public static bool checkWoWPointStaticStay(WoWPoint point)
        {
            // Take 4 points around the one
            // we're going to test, check whenever the
            // Z difference is too high for staying on it
            WoWPoint up, down, left, right;
            up = down = left = right = point;

            up.Y += 1.0F;
            down.Y -= 1.0F;
            left.X -= 1.0F;
            right.X += 1.0F;

            // Calculate points height
            // Maximum tolerance 2 yards
            // If any of the height are above or under 2 yards
            // the check fails
            float tolerance = 2.0F;
            return findNearestPointHeight(up, up.Z - tolerance, tolerance) != null &&
                findNearestPointHeight(down, down.Z - tolerance, tolerance) != null &&
                findNearestPointHeight(left, left.Z - tolerance, tolerance) != null &&
                findNearestPointHeight(right, right.Z - tolerance, tolerance) != null;
        }

P.S.
I know there are at least 3 fishing bots already, but I'm building this for learning the APIs and to make some nice enhancements other bots don't have.
 
Last edited:
Thanks chinajade!

I've made this first attempt, but from my tests it results way too heavy to work (several seconds (around 10!) instead of milliseconds), and it not always return me a WoWPoint (i.e. no point found for pool) (when it founds it, it works as expected but the LOS I haven't implemented yet). May you, or any other member of the forum, help me optimizing it, or give me hints where to do things differently

There is a fundamental problem somewhere. It should be milliseconds of execution for even a hundred yards. The problem usually comes with the number of TraceLines a developer attempts, not their length. The four yard line you have should be very quick.

I don't have time to look at this atm, but here are some thoughts for you:
  • Read this.. Tau Day.
    You will never use radians again, and will thank the writer for the rest of your (programming) life.
  • You're use of Traceline looks spot on.
    TraceLines are directional. If you start the test from 'below water' or 'below ground' you'll usually get a (wrongly intended) hit immediately.
  • Prefer WoWPoint.Empty to new WoWPoint() or new WoWPoint(0.0, 0.0, 0.0).
    The former is slightly more efficient and semantically clear.
  • Blindly guessing at the problem area, Navigator.FindHeights() isn't returning what you expect.

The problem looks like a lot of fun! Enjoy solving it. :D


cheers,
chinajade
 
Last edited:
- Nice article, I'll read it all ASAP :)
- TraceLine: understood, but isn't my method calculating from top to bottom? And yes, it will detect ground as I'm forcing the line between "air" and "ground", but this is what I want to do: in case the "ground" is water only, the method returns false, while when it hits ground and eventually water, it means that the water is low and I can stand there to cast Fishing.
- I'm gonna use WoWPoint.Empty, thanks for the tip!
- If Navigator.FindHeights() is not returning what I expect, what should it return? AFAIK it should return all the possible heights (i.e. usually 1 up to x (I include option 0 just to include impossible coordinates), depending if there are caves or fluctuating terrains).
 
Last edited:
I found out what's making so much elaboration:
PHP:
            sw.Start();
            // Check whenever the line intersects ground or a structure
            bool groundOrLevel = GameWorld.TraceLine(top, bottom, GameWorld.CGWorldFrameHitFlags.HitTestGroundAndStructures);
            // Check whenever the line doesn't cross lava (we don't want to be cooked!)
            bool lava = GameWorld.TraceLine(top, bottom, GameWorld.CGWorldFrameHitFlags.HitTestLiquid2);
            DebugLog("Ground check for point {0}: {1}ms", point, sw.ElapsedMilliseconds);

According to the log:
PHP:
[19:12:49:838] Ground check for point <-9029.969, -1072.87, 135.0707>: 40ms
[19:12:49:911] Ground check for point <-9030.692, -1068.766, 135.0707>: 72ms
[19:12:49:999] Ground check for point <-9032.776, -1065.156, 135.0778>: 49ms
[19:12:50:064] Ground check for point <-9035.969, -1062.477, 135.0778>: 64ms
[19:12:50:093] Ground check for point <-9039.885, -1061.052, 135.0778>: 28ms
[19:12:50:126] Ground check for point <-9044.053, -1061.052, 135.0778>: 32ms
[19:12:50:160] Ground check for point <-9047.969, -1062.477, 135.0778>: 33ms
[19:12:50:194] Ground check for point <-9051.161, -1065.156, 135.0778>: 33ms
[19:12:50:226] Ground check for point <-9053.245, -1068.766, 135.0707>: 32ms
[19:12:50:260] Ground check for point <-9053.969, -1072.87, 135.0707>: 33ms
[19:12:50:293] Ground check for point <-9053.245, -1076.974, 135.0707>: 32ms
[19:12:50:327] Ground check for point <-9051.161, -1080.583, 135.0707>: 33ms
[19:12:50:360] Ground check for point <-9047.969, -1083.262, 136.0605>: 32ms
[19:12:50:393] Ground check for point <-9044.053, -1084.688, 136.888>: 33ms
[19:12:50:426] Ground check for point <-9039.885, -1084.688, 137.2368>: 32ms
[19:12:50:460] Ground check for point <-9035.969, -1083.262, 137.0857>: 33ms
[19:12:50:493] Ground check for point <-9032.776, -1080.583, 136.2615>: 33ms
[19:12:50:526] Ground check for point <-9030.692, -1076.974, 135.0707>: 32ms
[19:12:50:560] Ground check for point <-9028.969, -1072.87, 135.0707>: 33ms
[19:12:50:593] Ground check for point <-9029.753, -1068.423, 135.0707>: 32ms
[19:12:50:626] Ground check for point <-9032.01, -1064.514, 135.0778>: 33ms
[19:12:50:659] Ground check for point <-9035.469, -1061.611, 135.0778>: 33ms
[19:12:50:693] Ground check for point <-9039.711, -1060.067, 135.0778>: 32ms
[19:12:50:728] Ground check for point <-9044.227, -1060.067, 135.0778>: 35ms
[19:12:50:783] Ground check for point <-9048.469, -1061.611, 135.0778>: 54ms
[19:12:50:847] Ground check for point <-9051.928, -1064.514, 135.0778>: 64ms
[19:12:50:921] Ground check for point <-9054.185, -1068.423, 135.0707>: 73ms
[19:12:50:985] Ground check for point <-9054.969, -1072.87, 135.0707>: 64ms
[19:12:51:042] Ground check for point <-9054.185, -1077.316, 135.0707>: 56ms
[19:12:51:107] Ground check for point <-9051.928, -1081.226, 135.0707>: 64ms
[19:12:51:173] Ground check for point <-9048.469, -1084.128, 136.2461>: 65ms
[19:12:51:237] Ground check for point <-9044.227, -1085.672, 137.1437>: 63ms
[19:12:51:308] Ground check for point <-9039.711, -1085.672, 137.5448>: 71ms
[19:12:51:370] Ground check for point <-9035.469, -1084.128, 137.3951>: 61ms
[19:12:51:442] Ground check for point <-9032.01, -1081.226, 136.5926>: 71ms
[19:12:51:500] Ground check for point <-9029.753, -1077.316, 135.1052>: 57ms
[19:12:51:573] Ground check for point <-9027.969, -1072.87, 135.0707>: 72ms
[19:12:51:639] Ground check for point <-9028.813, -1068.081, 135.0707>: 65ms
[19:12:51:704] Ground check for point <-9031.244, -1063.871, 135.0778>: 65ms
[19:12:51:769] Ground check for point <-9034.969, -1060.745, 135.0778>: 63ms
[19:12:51:835] Ground check for point <-9039.538, -1059.082, 135.0778>: 65ms
[19:12:51:900] Ground check for point <-9044.399, -1059.082, 135.0778>: 64ms
[19:12:51:958] Ground check for point <-9048.969, -1060.745, 135.0778>: 57ms
[19:12:52:022] Ground check for point <-9052.693, -1063.871, 135.0778>: 63ms
[19:12:52:086] Ground check for point <-9055.124, -1068.081, 135.0707>: 63ms
[19:12:52:151] Ground check for point <-9055.969, -1072.87, 135.0707>: 64ms
[19:12:52:216] Ground check for point <-9055.124, -1077.658, 135.0707>: 64ms
[19:12:52:280] Ground check for point <-9052.693, -1081.869, 135.0707>: 64ms
[19:12:52:344] Ground check for point <-9048.969, -1084.994, 136.4318>: 63ms
[19:12:52:409] Ground check for point <-9044.399, -1086.657, 137.3995>: 64ms
[19:12:52:474] Ground check for point <-9039.538, -1086.657, 137.8311>: 64ms
[19:12:52:539] Ground check for point <-9034.969, -1084.994, 137.7362>: 64ms
[19:12:52:603] Ground check for point <-9031.244, -1081.869, 136.9236>: 63ms
[19:12:52:678] Ground check for point <-9028.813, -1077.658, 135.227>: 74ms
[19:12:52:734] Ground check for point <-9026.969, -1072.87, 135.0707>: 55ms
[19:12:52:800] Ground check for point <-9027.873, -1067.74, 135.0707>: 65ms
[19:12:52:866] Ground check for point <-9030.479, -1063.228, 135.0778>: 65ms
[19:12:52:930] Ground check for point <-9034.469, -1059.879, 135.0778>: 63ms
[19:12:52:996] Ground check for point <-9039.364, -1058.098, 135.0778>: 64ms
[19:12:53:059] Ground check for point <-9044.573, -1058.098, 135.0778>: 63ms
[19:12:53:125] Ground check for point <-9049.469, -1059.879, 135.0778>: 65ms
[19:12:53:191] Ground check for point <-9053.459, -1063.228, 135.0778>: 65ms
[19:12:53:255] Ground check for point <-9056.064, -1067.74, 135.0707>: 64ms
[19:12:53:326] Ground check for point <-9056.969, -1072.87, 135.0707>: 70ms
[19:12:53:393] Ground check for point <-9056.064, -1078, 135.0707>: 66ms
[19:12:53:450] Ground check for point <-9053.459, -1082.512, 135.0752>: 56ms
[19:12:53:515] Ground check for point <-9049.469, -1085.86, 136.6256>: 64ms
[19:12:53:587] Ground check for point <-9044.573, -1087.642, 137.624>: 71ms
[19:12:53:646] Ground check for point <-9039.364, -1087.642, 138.0966>: 58ms

It is using too much elaboration time, or I'm calculating too many spots. Any hint? I'm actually checking for 1 yard to 1 yard checks, 20 degrees (for a total of 18 checks per circle)...
 
I need to go back to school, the math of all this hurts my head :'(.
 
It is using too much elaboration time, or I'm calculating too many spots. Any hint? I'm actually checking for 1 yard to 1 yard checks, 20 degrees (for a total of 18 checks per circle)...

Looking at your code in Post #8, you're checking 11 concentric circles at 18 times per circle. Estimating the average Traceline execution time you've measured at 50ms, indeed it does come out to a 10sec delay.

You've bumped into the classic problem hinted at in Post #9: "The problem usually comes with the number of TraceLines a developer attempts, not their length".

There are two ways to tackle the problem...
  • Convert your for loops to build the structures needed to do a MassTraceLine.
    The time to populate a data structure is negligible compare to the time to do the TraceLine. So, it may be possible that MassTraceLine is not simply a 'convenience function', but can actually do things in parallel that will give you a performance benefit. To be honest, I've never measured this, and would love to know your findings if you choose to pursue this.

  • Be smarter on your Tracelines
    The key is to reduce the number of Tracelines you need to execute. You should be able to rule out much of the outer circles based on your inner circles telling you 'isground' or 'iswater'.
    Also, you may have a way to calculate the 'direction' between the toon (standing at the center of the circle), and the location of the pool. If so, you only need to check (say +-15 degrees) to each side of the pool. I assume you're trying to answer the question of "did the bobber hit the pool?", not "where is the pool?"
    In short, you need to find a way to use other (cheaper to obtain) information from your environment to cut down on the number of Tracelines needed.


cheers,
chinajade
 
Could you explain what it is you are trying to achieve. Is it as ChinaJade suggested "test if bobber hit the pool" or is it "can I navigate to fish from the pool from my landing location" ? I may have a suggestion or two for the latter.
 
Actually I'm creating the circles around the pool, searching for the nearest (or "first ground hit") point where to land out of it, not out of the player :)

I've got two ideas in mind:
1. reduce to 10 casts (36? up from 20?) down from 18 per circle. I may increase degrees to 45, making 8 casts too, I have to test it and check if it's not too distant point to point.
2. reduce 4 the circles down from 11 (3 yards up from 1)

1000ms = 10 -> 13 yards covered
or (45?)
1200ms = 10 -> 16 yards covered

I'm going to test mass trace line too, maybe every *traceline loads and unloads the mesh data, thus if this is true, masstraceline is way cheaper than 8-10 tracelines (per circle)

EDIT:
PHP:
[20:45:07:945] I took 741ms to calculate the nearest landing point for pool at <-9063.41, -1051.5, 136.6761>
Sounds good so far :) (45? with single TraceLine(s)), even if I think 45? are too much for 10 yards
 
Last edited:
Actually I'm creating the circles around the pool, searching for the nearest (or "first ground hit") point where to land out of it, not out of the player :)

I've got two ideas in mind:
1. reduce to 10 casts (36? up from 20?) down from 18 per circle. I may increase degrees to 45, making 8 casts too, I have to test it and check if it's not too distant point to point.
2. reduce 4 the circles down from 11 (3 yards up from 1)

1000ms = 10 -> 13 yards covered
or (45?)
1200ms = 10 -> 16 yards covered

I'm going to test mass trace line too, maybe every *traceline loads and unloads the mesh data, thus if this is true, masstraceline is way cheaper than 8-10 tracelines (per circle)

I've been working on a similar issue with my plugin GiftOfFlight. My method seems similar to yours but instead of working on a spiral/circle approach, I've gone for a nested for loop rectangle approach. Maybe you could intergrate some of my ideas if you want.

My system works like this (simplified for obv reasons)


PHP:
WoWPoint target = place I want to land;
 
for (int x = -50; x <= 50; x++)
for (int y = -50; y <= 50; y++)
{
heights = Navigator.FindHeights(target.X + x, target.Y + y);
z = find heighest point; //for loop or what ever you choose
if Navigator.CanNavigateFully(target, new WoWPoint(target.X + x, target.Y + y, z))
{
store this point as a candidate
}
 
}

I obvously use a multiplier to space the locations checked, as WoWPoint.X + 1 = 0.2 yards in game, so roughly the same spot in practice.

The major issue I found is if your target is deep underground/building.

Also Flightor likes a nice clear approach to the landing site, so tight to the side of a building will not do.

Note, I have no formal training with C# so there is a good chance my code is of a poor standard, but I'm just here to learn.

Hope it may help in some way
 
I've been working on a similar issue with my plugin GiftOfFlight. My method seems similar to yours but instead of working on a spiral/circle approach, I've gone for a nested for loop rectangle approach.
...&lt;snip&gt;...

Note, I have no formal training with C# so there is a good chance my code is of a poor standard, but I'm just here to learn.

Absolutely nothing wrong with rectangles. :D

For no formal C# training, you picked a heck of an ambitious project to start. Kudos to you!
I've yet to try the Gift of Flight, but it definitely caught my eye. I fully intend to give it a whirl when getting a chance to get back to botting.

Thanks for tackling a tough problem that's been a thorn in the Community's side for almost a year.



cheers,
chinajade
 
Problem with your code is that it will first check for far points, instead of starting from the nearest ones. BTW I think your code is just the same as mine to determine candidates, the hit ground check is the one taking time :)
 
Problem with your code is that it will first check for far points, instead of starting from the nearest ones. BTW I think your code is just the same as mine to determine candidates, the hit ground check is the one taking time :)

Yea, that is a drawback of my system. I chose it over concentric circles because over a large area you'd need a scalable totalChecksPerRound else the further away the points being checked are, the larger the gaps will be between them. I thought that to keep an even spread would be too much math for me to deal with.

Also have you had any trouble with Navigator.FindHeights() ? I find that on occasion I get false positives from it when dealing with a target that is underground or in a building.

After a closer look at your sample code a few posts back I see you are trying to find the best landing spot "on land" which is taking the time. How about find a landing spot, then in a new thread check for ground surface type whilst the bot is descending, this may help to streamline the processing and reduce percieved processing time, as it can be calculating the best fishing spot during the time it takes to land. Then if it is still descending when the calculation is complete, interupt the descent with the new landing spot. Or if it has descended fully by the time the calculation has finished navigate by ground mount to the fishing spot.

I am concidering this for my plugin as my flight times are usually 20 seconds or more. If I just get Flightor heading in the right direction and then do the calculation during the flight and feed the chosen spot back into Flightor mid flight.
 
Of course you can use delegates... maybe a meta-code like this:

PHP:
WoWPoint[] foundPoints = point_created_via_circle_math; // takes really few time
bool[] results;
myFunctionWhichHasACycleAndCallsNTimesMyThreadedCalculatorMethod(out results);
do {
    for(int i=0; i< results.Count; i++)
        if(results[i] == true) break;
    if(results[i]) break;
} while (results.Count < foundPoints.Count);
if(results[i])
    // foundPoints[i] is the landing spot

EDIT: thinking about this, we have no guarantee that results will be in the same order, maybe we need a Dictionary<WoWPoint,bool?> instead, so that when all bools are not null you can exit the while cycle.
 
Last edited:
Back
Top