Distance formula to calculate speed between two points
I've spotted the InFlight thread about posting distance results of flights, so I wrote a script that parses the DBC data to extract this information, but I am stuck making this function properly:
Code:
// get delay between two points on a map and the movement speed modifier If I get this to work sure I'll post source and best of all no flight addon will have to calculate on the fly, the time it will take to fly the path! One can in advance of a patch calculate the data from the DBC themselves and produce a LUA database with all the exact timers, cool right? |
That would work, assuming that all flight paths are perfectly straight lines.
|
The way that InFlight and other timers work is by users recording how long it takes to fly from point A to point B. If a new flight is not in the addon's database, then it just measures how long it takes and stores that for next time.
|
If you have absolute coordinates, you don't need MapID. You're also using a bunch of unneeded trig functions.
PHP Code:
|
(Don't forget that we do not have access to z-coordinates.)
|
Thing is if the path isnt a straight line then you add more nodes and make the game slightly wobble (so it's not instant turns) but knowing the distances and angles between two coordinates would also allow you to figure out this wobble and include it in the time delay calculation, so you can achieve calculating the arrival time trough a path of nodes using their distances between each other.
For now my problem is that the paths have mapId relative coordinates so I have to figure out how big let's say northrend is, then outlands, e.g. Looking at MapDataLib for some info but havent gotten a proper formula, yet. Anyway, there are advantages to calculating the times liek this, for once you can get all times without having to fly yourself to "time" it. :) *Edit* Saw your post SDPhantom, thanks. Gonna work it in, hehe. Yeah, I'm a bit unclear about the formulas, hence they look messy. ;) The coordinates in the DBC are raw, but not sure if it maters the size of the map, I guess not as long we dont fly across one map to the other (like to azuremyst but thats another problem to figure later on, hehe). |
I don't understand what you mean by "nodes" and "wobble"?
|
Quote:
Quote:
|
It's a bit hard to explain, so I'll show some examples of what I mean;
TaxiNodes.dbc contains the columns: TaxiId, mapId, x, y, z, name, ... TaxiPath.dbc contains the columns: PathId, fromTaxiId, toTaxiId, cost TaxiPathNode.dbc contains the columns: PathNodeId, PathId, counter, mapId, x, y, z, ... Combining these information tables you can combine a start and a destination, for example in TaxiNodes.dbc Stormwind is #2 and Goldshire is #582. Then you check TaxiPath.dbc if you have a PathId for a path fromTaxiId=2 and toTaxiId=582, if not then that taxi path does not exist. If you do on the other hand, you look the nodes in the path (the mount follows these node by node) in TaxiPathNode.dbc where you look up all PathNodeId where PathId=TaxiPath.PathId and you get a list where counter goes from 0 up to the last node (SW->GS has 16 nodes along the flight path). Each flight position has coordinates, they are relative to the map and we know the mapId in case we need to translate coordinates (i.e. flying from one map to the other, e.g. maybe it affects the distance calculations, not sure, maybe not). Anyway, mathematically you can iterate a path of many nodes and each step you take you compare this with the previous (or next, depending how you program it) and get a distance difference by putting the coordinates in a formula, then if you know how fast you are moving you can calculate the time between each node and sum them all up for the total travel time from start to finish. The only problem so far is the possibility of a "wobble", like this: Fig. 1 is how the math would calculate distances, straight lines from one point to the other. Fig. 2 is how it feels in the game, you don't fly and instantly turn once you hit a node, instead the game does some sort of wobble to make the transition smooth between nodes. TaxiNodes = positions you can land and take off FlightNodes/(I call them nodes in this post) = the orange dots in between the air, not visible but you go from one to the next like waypoints. *Edit* SDPhantom the coordinates are on this form (one random line from both files that contain coordinates): 2,0,-8841.05957031,489.656005859,109.607002258,"Stormwind, Elwynn",0,541,1,0.00998003967106,0.0101744187996, 35,6,0,0,-8852.65039063,496.459991455,111.040000916,0x0,0,0,0, You have the mapId (0 in this case) and the coordinates, it does look like continent coordinates, yes. *Edit 2* Just figured I'd put an example output so far, the time is about right for this specific path, but need to find the speed value, it may be variable or constant if lucky. At the moment the speed was set to 30.33... but need to test with more FP to see if it applies to them or not, I think not because of this wobble I mentioned in the post. :( Quote:
|
Quote:
In Azeroth, there are only a handful of such mounts it chooses on, which are faction-based and mostly fly the same speed. I think Outland is the same. Where you really see a difference is in Northrend. For example, one flight path would give you a gyrocopter to ride on which is the slowest among the list, while another would give you a dragon mount, which are the fastest. |
Quote:
Have you actually verified this or is this a conclusion based on your observations of how fast it feels? I hear people comment on how some mounts feel slower or faster relative to other mounts all the time, but this isn't actually the case. How fast something feels is an imprecise method of measurement. |
Try flying Sholazar Basin, it's absolutely verified.
|
Quote:
|
Must be some way to do this from the DBCs considering they define the distances and flight related paths...
*Edit* Good news, tested it flying between SW, Goldshire and Eastvale and the timers expected are about right, only that in-game there can be up to a second delay when you click and actually start to fly, e.g. or between path transitions (i.e. sw to goldshire then it has to hop you on from goldshire to eastvale, that transition can take enough time to add a couple of seconds to the total timer from start to finish if you fly and jump many different paths). Going to make a function to iterate trough the various possible paths in-game, that exists of course, then store it in a file and check with the current InFlight.lua data and see how much off I am, hehe. If lucky and it's only some seconds differences then it may be my speed constant that needs changing, or changing depending on the path, there are some flags I am not sure what are on taxinodes so if lucky they hold the answer to the speed problem. :) *Edit 2* These are the times for single flights only (no connections): http://pastebin.com/PJLHijdG (from, to, copper, time) I've looked in the inflight data and must say it's scary how similar the times are, only a few weird entries like Sentinel Hill to Stormwind is 25 sec on inflight while around 80 on the calculations. :P For chained flights one has to think about the start->node1 and nodeN->end times and skip them on paths in between, i.e. if you fly Stormwind -> Duskwood -> Blasted Lands one would have to skip the nodeN->end (landing) time between Stormwind and Duskwood, Duskwood and Blasted Lands you need to skip both launch and landing, and at the end you must skip the launch->node1 while include the nodeN->landing times to get it most accurate as you can. In the DBC the taxi path names are localized so it's a bit tricky to make it easy to work on many locales, considering the ID in the DBC are not accessible trough game API (I think they should make it tough, hehe). Anyway, these are my findings so far. I'll release my PHP script for the data management and such soon, it's not a big deal, and it's actually a bit noobish in layout. :( Made it mainly to extract data from DBCs (actually their CSV files after using another program to convert the DBCs) to begin with. About making the flightmaps tough, this shouldn't be too hard: 1. We have a list of all from and to paths, so all you have to do is scan this array of data and check "I am at X, when is from=X and store the to value in a table with a index of X" thus you end up with for example: {[2]={5,6,8}} where at node 2 you have access to nodes 5, 6 and 8. Then you can do a debt query and go further down, check where does 5 go to, where do 6 go to, e.g. When at the FP in-game you know where you start and you know where you want to go, and the game marks the path so you know the nodes in between, so with something smart you can recursively find these path names and by having some sort of library to convert the names into ID's for example, you can extract the times from the database above and sum up the different times. I realize you loose the launch->node1 and nodeN->landing times but assume 1+-2 seconds differences in the mathematical from the actual version. Besides, latency also plays a role, if you have 500ms you will notice slight alteration in the times anyway, you can't get the times 100% accurate, only estimates so it's not a bad idea to use the raw data to generate the flight times I think. :) Sorry for blocks of text. Is there any point me writing this btw? I got the impression someone else is benefiting from this information, maybe not, maybe it's just me... |
The flightpath speeds are constant per flightpath: The Sholazar flightpath is substantially slower than others.
|
Each taxi location has a creature ID (some are 0, not sure what that means really...) anyway these are the current creature ID's:
http://pastebin.com/DHyu3BZY Somehow one has to extract the information about their speeds from the server, because the DBC do not contain this information. For example: http://db.mmo-champion.com/c/54271/ |
Quote:
|
That's what I was trying to express, but I was a bit terse about it so I failed miserably. :D
|
1 Attachment(s)
If lucky the speeds can be read from the creature when flying... but considering when you mount you can't directly query the creature itself, this will be a bit hard, or maybe it's just a creature ID for the visuals, the speeds may be hardcoded in the exe maybe, since it's a bit unlikely that they are in the DBC, but maybe you are more awesome then me, so I put the files in a zip if you like to peak at the source data. :)
|
It's readable with a call to GetUnitSpeed("player") while on a taxi.
|
It's not quite on topic, but let me ask you smart guys;
This is the data: A -> B B -> C C -> D E -> C A -> E B -> A C -> B C -> A A -> C You see, if you wish to go from A to D you can go: (1) A->B->C->D (2) A->C->D (3) many others involving cycles like A->C->B->A->C->D and so forth... In any case, I have this system in MySQL: {PrimKey, X, Y} where PrimKey is an integer, X and Y are the node values. For example A->B is {1, A, B}, then B->C becomes {2, B, C} and so forth. This is graph theory, traversal of graphs but considering there are cycles this makes it a bit tricky. I have to make a class i.e. "Map" to help me keep order of who comes and goes to who. This way if I want to go from A to D I can return a list where I get various paths leading to D without the backtracking (A->C->B->A->C->D e.g.) and it shouldn't stuck looping indefinitely. Do you have some ideas how I should structure this class? I was thinking something along the lines of: class Map { nodes = array(); addNode(node) { addChild(getParent(node::letter), node) // find A in nodes then add this node as that parents child, if not then add the child as a new root-node with node::letter as index key } getRoutes(start, end) { // traverse the nodes, find any paths starting at start and ending at end, paths not leading to the end are ignored, this I reckon will be a recursive function } } Very rough, maybe someone has a better idea or a tip like example code they know of on the net. :P |
A method I could think of is to have it scan out, branching off every time it has a choice based on direction from the origin point. I've had experience in writing functions that run recursively though data without the need to call itself. This usually involves building and managing your own data in a stack-like data structure. You end up using more memory instead of increasing your call stack, which is the best way to avoid stack overflows. It'll take a while to write it all from scratch. I'm assuming this would be in PHP?
|
Indeed, php. :(
|
I'm looking into writing some code that'll allow you to add paths with a cost metric and the pathfinding AI will choose the lowest cost route. To conserve processing time, it'll "give up" on a path if its cost exceeds that of a route it already discovered.
This method will allow you to customize what you use for a path's cost metric, whether it's the copper cost, distance, or even number of hops. The first two should be easy to figure out, but to do hops, you'll need to set the cost of all paths to a single number greater than zero, like 1. I may have the pathfinding AI accept a max cost argument that it'll automatically "give up" on if the cost is exceeded. |
I have a working prototype running and here's the debug info that it's printing.
The following calls were made to generate this. Code:
addPath("A","B",1); Path Data: Code:
Create: A>B(1) Routing Steps: Code:
Routing B>E Returned Route: Code:
Array |
Polished off a few checks and removed the debug code. Here's the prototype.
PHP Code:
addPath() returns true on success, false on duplicates, and NULL on error. findRoute() returns an array on success, false if no path found, and NULL on error. |
Wow SDPhantom, you really put effort into this code, thanks!
I got the basic idea, just trying to sort off cycles now, as it gets stuck looping in a circle thinking it goes forward. :D I need to add a "visited" check to skip going where we've already been I reckon. |
Strange, can you post a copy of the paths you're registering? When it checks a path, it looks back into the stack to see if the node isn't already in there. With a bigger data set, it would often recheck paths again when going back up the stack. You can try setting the maxcost to limit how far it runs away with this.
|
http://pastebin.com/e0LfewY1
For example if I do this: findRoute(2, 15); The output of the id is this: Code:
0>1>1>2>2>2>3>3>3>3>4>4>4>4>5>5>5>5>5>6>6>7>7>7>6>5>6>6>6>6>5>4>5>6>7>7>7>7>7>7>8>8>8>8>7>6>6>5>5>4>5>6>6>6>7>7>8>9>9>10>10>11>11>11>12>12>12>12>13>13>14>14>14>14>15>15>16>16>16>16>16>17>17>18>19>20>20>21>21>22>22>23>24>25>25>26>26>26>26>27>27>27>28>28>28>29>29>29>30>31>31>31>31>30>30>29>28>27>27>28>29>29>30>31>31>31>31>30>30>30>29>28>28>28>27>... |
The stack ID only reflects the size of the stack at the time, it'll always be going up and down until it's all done. It's pretty much equal to how many hops it's at in its current path.
|
I looked at the DBC data over and discovered that no paths lead to #15. I added in a check for this before it starts pathing and an additional maxhops argument. I would suggest using it to cut down on processing time. You may use NULL to bypass the maxcost argument.
For example, this'll trace an alliance route from Booty Bay to Light's Hope Chapel with a max of 10 hops. Code:
findNode(19,67,NULL,10); PS: You should only load in paths accessible to a specific faction to help in processing if by chance it's queried for a route cross faction. It can also wander over into cross faction routes if it hits shared paths. It seems the value following the unknown horde/alliance IDs is indeed a 3-bit faction flag. Bit 1 is alliance and bit 2 is horde. I see bit 3 set in a few paths, but it's unknown what that means at this point. |
Hmm, really? Maybe I used a node no longer accessible, should have used the lighthope chapel one, sorry for my mistake too, kind of set you on the wrong path.
Yes, you are right about the faction thing, should not mix them up. *Edit* Very nice mate, couldn't have done this all by myself -tough I should, had graph theory in Java a year ago. :( Anyway, got it working on my end as well, made a class for it and it only reads in paths that are accessible for the faction when I am flying around in my virtual WoW. ;) *Edit 2* I ran some calculations for 10 paths from Stormwind, the results were (time in HH:MM:SS): Code:
Stormwind, Elwynn -> Sentinel Hill, Westfall takes 00:01:11 Code:
Stormwind, Elwynn -> Sentinel Hill, Westfall takes (71-73) -2 With some fine tuning like adding delay because of the "wobble" effect (large angles, high distances, create a slight deviation in the distance calculation, this is not yet addressed in the code), and proper faction checks to allow neutral paths when flying as any faction but restrict the path finding to either Alliance or Horde and avoid mixing those. Perhaps these huge deviations may be due to result of flights going quicker than the constant speed I calculate with (30+1/3), maybe those paths used quicker flight speeds, so this also has to be added in the code. This was my "report" on the matter for now, when this is done I'll share my code for anyone to use that needs to calculate taxi flight times. :P |
Do your calculations account for when the taxi circles before landing?
|
Yes it should, hard to tell atm because I need to implement the various speeds on some flightpaths, hehe.
For many regular gryphon based flightpaths with regular speeds the calculations return answers within 3 seconds of the InFlight data, I guess the last 3 seconds are due to latency; it takes a up to a second to start flying and landing and some minor delays due to wobble effect or when jumping from a node to node in general. I think Ironforge may be the cause of the weird timers on my end, probably because the game does not fly in the city when flying on a connected path, it skips some nodes that I include in the calculations most likely, need to research more. :) |
Ah, yes, that would definitely be a contributing factor. SW, too.
|
Something I noticed about GetUnitSpeed:
The API returns different values but very small changes, decimals only. Still, at long paths this may change the time up to 10 seconds at worst, maybe this has something to do with lag compensation? I.e. if the average player ms is 500 then the game would make up for the time lost in between node connections (i.e. sw->goldshire->redrige), each "->" jump would need to be confirmed with the server so if the mount visually flies quicker and the client comes at the last node and requests the next path, it has accounted for the slight delay ocuring so you wouldn't loose time. Somehow it doesn't make any sense because the location of the player is anyway decided by the server, that explains why I've sometimes seen me land earlier than I should by having my flight just teleport me the last 40 yards instead of flying and landing like it should have. The gryphons (creature 541) flies between 30.1 to 30.4 so any decimals in between, very weird and hard to account for when flying, thus even tracking the time with a stopwatch would always return 1-3 seconds differences between flights due to this randomness... Still, some seconds off the mark is not a big deal considering you still know you got over 13 minutes of flying if you want to fly from Booty Bay to Eastern Plaguelands. ;) |
4 Attachment(s)
I spent a weekend trying to figure out the flight-path mechanics, and while I did come pretty close to accurately modeling them, I'm stumped by a few issues. Since I doubt I'll make anymore progress without a big time commitment, here's what I've found so far:
Overall it's still pretty inaccurate because of lag, unknown speed, and the path joint issue. Oh well, it was still a fun project. :) My test mod is attached if anyone wants to play with it. "/taxi" opens the grid, and you can navigate around sort of like Google Maps. If you talk with a flight master while the grid is open, hovering over nodes will plot their expected paths. |
Very informative post, thanks for sharing your findings!
Very interesting, catmull-rom splines, haven't heard of that until now, gotta look into this! |
All times are GMT -6. The time now is 10:47 PM. |
vBulletin © 2024, Jelsoft Enterprises Ltd
© 2004 - 2022 MMOUI