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

How about :

Class inherits WoWPoint
MyWoWPoint : WoWPoint

instance variable result
bool result = false;

MyWoWPoint[] foundPoints = point_created_via_circle_math;
call your function and populate foundPoints.results;
etc
etc
 
Why should you create a class child just for a variable? Dictionary is good enought :)
 
Ok this method should work nicely, I still need to create the threaded method (working on it)

PHP:
        public static WoWPoint getNearestLandingPointFromPool(WoWObject pool)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            
            LocalPlayer Me = ObjectManager.Me;
            WoWPoint candidate = new WoWPoint();
            
            Dictionary<WoWPoint, bool?> candidatePoints = new Dictionary<WoWPoint, bool?>();
            // Circles cycle
            // from 10 to 20 yards, step = 3 yards
            // This will fill the possible candidates
            for (int range = 10; range <= 20; range += 3)
            {
                // 35? (10yrd) -> 25? (20yrd)
                float radians = WoWMathHelper.DegreesToRadians(35.0F + 10.0F - (float)range);
                int totalChecks = (int)(2 * Math.PI / radians);
                for(int i = 0; i < totalChecks; i++)
                {
                    candidate = pool.Location.RayCast(radians * i, range);
                    float? height = findNearestPointHeight(candidate, pool.Location.Z);
                    if (height != null)
                    {
                        candidate.Z = height.Value;
                        candidatePoints.Add(candidate, null);
                    }
                }
            }
            // No candidates found
            if(candidatePoints.Count == 0)
            {
                DebugLog("No landing candidate found for pool at {0}", pool.Location);
                return WoWPoint.Empty;
            }
            // Check the candidates via a threaded method
#warning TODO: threaded method

            bool allDone = false;
            bool checkAllNotNull = true;
            // Wait for all points to be calculated
            // Avoid infinite cycle if something goes wrong
            // using a timer (20 seconds)
            Stopwatch maxTime = new Stopwatch();
            sw.Start();
            while (!allDone || maxTime.ElapsedMilliseconds < 20000)
            {
                checkAllNotNull = true;
                foreach (KeyValuePair<WoWPoint, bool?> check in candidatePoints)
                    if (check.Value == null)
                    {
                        checkAllNotNull = false;
                    }
                if (checkAllNotNull) allDone = true;
            }

            if (!checkAllNotNull)
            {
                DebugLog("Calculations took too much time, desisting finding a landing candidate for pool at {0}", pool.Location);
                return WoWPoint.Empty;
            }

            // search for the nearest candidate, if any
            candidate = null;
            foreach (KeyValuePair<WoWPoint, bool?> pair in candidatePoints)
            {
                if (pair.Value.Value == true && candidate == null || Me.Location.Distance(pair.Key) < Me.Location.Distance(candidate))
                    candidate = pair.Key;
            }

            if (candidate != null)
            {
                DebugLog("I took {0}ms to calculate best landing point for pool at {1}", sw.ElapsedMilliseconds, pool.Location);
                return candidate;
            }
            DebugLog("No landing candidate found for pool at {0}", pool.Location);

            // No valid candidates found
            return WoWPoint.Empty;
        }
 
Ok, MassTraceLine is waaaaaaaaaaaaaaaaaaaaaaaaaaay convenient. I'm gonna write the method later (3 different checks per WoWPoint, 38 points, 83 ms total :) )

P.S.
Just for the chronicles, TraceLine is high-stress, CPU-bound, this means that even with threads it was taking too much time.
 
Ok, MassTraceLine is waaaaaaaaaaaaaaaaaaaaaaaaaaay convenient. I'm gonna write the method later (3 different checks per WoWPoint, 38 points, 83 ms total :) )

P.S.
Just for the chronicles, TraceLine is high-stress, CPU-bound, this means that even with threads it was taking too much time.

Great news! That looks substancially faster :)

I'd be really interested to learn more about TraceLine and MassTraceLine as it's not something I've encountered yet. May I see an example of your code to point me in the right direction please.
 
This definitively works. I just have to optimize checkWoWPointStaticStay, but I don't know if it's my dismount method which is making me dismount before reaching the point, or it's Flightor which is not moving to exact WoWPoint, but this is another story :)

I may even make more steps per round, as 100+ points take me less than 100ms (on my 4.5 Ghz quad-core CPU though)

PHP:
        public static bool checkGroundUnderWoWPoint(WoWPoint point, WoWPoint pool)
        {
            // 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 -= 4.0F;

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

            Stopwatch sw = new Stopwatch();
            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);
            bool LOS = GameWorld.TraceLine(point, pool, GameWorld.CGWorldFrameHitFlags.HitTestLOS);

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

        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 += 3.0F;
            down.Y -= 3.0F;
            left.X -= 3.0F;
            right.X += 3.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;
        }

        public static WoWPoint calculateLandingPoint(WoWPoint pool, List<WoWPoint> candidates)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            SharedData sd = SharedData.Instance;
            bool[] groundResult, liquid2Result, losResult = {};
            List<WorldLine> lines = new List<WorldLine>();
            WoWPoint selected = WoWPoint.Empty;

            foreach (WoWPoint point in candidates)
            {
                WoWPoint top, bottom;
                top = bottom = point;
                top.Z += 3.0F;
                bottom.Z -= 2.0F;

                lines.Add(new WorldLine(top, bottom));
            }

            GameWorld.MassTraceLine(lines.ToArray(), GameWorld.CGWorldFrameHitFlags.HitTestGroundAndStructures, out groundResult);
            GameWorld.MassTraceLine(lines.ToArray(), GameWorld.CGWorldFrameHitFlags.HitTestLiquid2, out liquid2Result);
            GameWorld.MassTraceLine(lines.ToArray(), GameWorld.CGWorldFrameHitFlags.HitTestLOS, out losResult);

            for (int i = 0; i < groundResult.Count(); i++)
            {
                if (groundResult[i] && !liquid2Result[i] && losResult[i] && checkWoWPointStaticStay(candidates[i]))
                {
                    if (selected == WoWPoint.Empty || ObjectManager.Me.Location.Distance(candidates[i]) < ObjectManager.Me.Location.Distance(selected))
                        selected = candidates[i];
                }
            }

            DebugLog("I took {0}ms to calculate {1} WoWPoints.", sw.ElapsedMilliseconds, candidates.Count);

            return selected;
        }

        public static WoWPoint getNearestLandingPointFromPool(WoWObject pool)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            /* Check all around the pool in circles, with variable angles,
             * searching for a valid point where to cast
             * Fishing, starting from 10 up to 20 yards (step: 1 yard).
             * 
             * Return the nearest WoWPoint if found, WoWPoint.Empty otherwise.
             */

            LocalPlayer Me = ObjectManager.Me;
            WoWPoint candidate = new WoWPoint();
            List<WoWPoint> candidates = new List<WoWPoint>();
            SharedData sd = SharedData.Instance;

            DebugLog("Searching for a valid landing point around pool at {0}", pool.Location);

            // Circles cycle
            // from 10 to 20 yards, step = 1 yards
            // This will fill the possible candidates
            for (int range = 10; range <= 20; range++)
            {
                // 35? (10yrd) -> 25? (20yrd)
                float radians = WoWMathHelper.DegreesToRadians(35.0F + 10.0F - (float)range);
                int totalChecks = (int)(2 * Math.PI / radians);
                for (int i = 0; i < totalChecks; i++)
                {
                    // Before inserting the point, calculate its height
                    candidate = pool.Location.RayCast(radians * i, range);
                    float? height = findNearestPointHeight(candidate, pool.Location.Z);
                    if (height != null)
                    {
                        candidate.Z = height.Value;
                        candidates.Add(candidate);
                    }
                }
            }
            // No candidates found
            if (candidates.Count == 0)
            {
                DebugLog("No landing candidate found for pool at {0}", pool.Location);
                return WoWPoint.Empty;
            }

            DebugLog("Calculating valid points ({0} possible candidates)...", candidates.Count);
            // Check the candidates via a threaded method
            candidate = calculateLandingPoint(pool.Location, candidates);
            if (candidate == WoWPoint.Empty)
                DebugLog("No landing point candidate found for pool at {0}", pool.Location);
            return candidate;
        }
 
Last edited:
Thanks for the look at your code. This is going to give me something to get my teath into when I finish work. :)
 
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).
Yes and no. To fully understand MassTraceline, you have to understand how HB is able to run functions within WoW. Every frame HB can execute some code inside WoW. When it is done with the code, execution (in WoW) is resumed and HB has to wait for the next frame to execute code. For developers this is abstracted into functions such as TraceLine and MassTraceline. If you use TraceLine, HB will have to wait for the ability to execute code before every call to TraceLine. Since HB only has this ability 1 time per frame, the time this wait takes is dependent upon FPS (up to 1000 / FPS milliseconds). MassTraceline, however, does all the tracelines in a single batch. This means MassTraceline is faster than TraceLine because it only waits for the ability to execute code once.

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.
I'm not 100% up to date with the flags, it's been a long time since they were reversed. However, I do know that HitTestBoundingModels does not hit actual models of creeps. It hits small structures in the world, like trees. HitTestLOS and HitTestGroundAndStructures are also composites of other flags.

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.

  • I see your Tau manifesto, and raise you a pi manifesto (click me). Pi is not wrong! Pi is beautiful. :D

    - 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).

    It's been a while since I looked at these things, but IIRC Navigator.FindHeights() returns the heights at a X/Y pair on the loaded mesh(es). If the mesh tile containing X/Y is not loaded, no results are returned (you can load tiles manually - check the API!).

    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

    There is no point of threading the point generation code. Thread overhead will probably take longer than the code itself. Also, calling TraceLine from multiple threads does not help; only 1 thread is able to use the executor at a given time - it won't actually be parallel.

    Ok, MassTraceLine is waaaaaaaaaaaaaaaaaaaaaaaaaaay convenient. I'm gonna write the method later (3 different checks per WoWPoint, 38 points, 83 ms total :) )

    P.S.
    Just for the chronicles, TraceLine is high-stress, CPU-bound, this means that even with threads it was taking too much time.
    See above. There is a reason for this.
 
For some reasons hitlos won't always return the LOS check! I had to disable it :-( any hint?

Inviato dal mio Nexus S usando Tapatalk
 
For some reasons hitlos won't always return the LOS check! I had to disable it :-( any hint?

Inviato dal mio Nexus S usando Tapatalk
LOS checks are only performed server side - there is no way to do it 100% correctly client side. Using TraceLine to do it is not always accurate.
 
I'll be posting a thread to explain a bit more about how we do code-execution in WoW. It should give developers a bigger "heads up" in terms of whats costly, and whats not. (Also ways to ensure you execute a bunch of code, in a single frame, instead of staggered through a bunch of frames)
 
Back
Top