mirror of
https://github.com/EQEmu/Server.git
synced 2026-09-04 18:46:37 +00:00
svn -> git Migration
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,591 @@
|
||||
/*
|
||||
|
||||
Fear Pathing generation utility.
|
||||
(c) 2005 Father Nitwit
|
||||
|
||||
|
||||
|
||||
Settings table:
|
||||
|
||||
CREATE TABLE fear_settings (
|
||||
zone VARCHAR(16) NOT NULL PRIMARY KEY,
|
||||
|
||||
#general settings:
|
||||
use_doors TINYINT NOT NULL DEFAULT 1,
|
||||
min_fix_z FLOAT NOT NULL DEFAULT 20,
|
||||
max_fear_distance FLOAT NOT NULL DEFAULT 250,
|
||||
image_scale TINYINT NOT NULL DEFAULT 4,
|
||||
|
||||
#path related
|
||||
check_initial_los TINYINT NOT NULL DEFAULT 0,
|
||||
split_invalid_paths TINYINT NOT NULL DEFAULT 0,
|
||||
link_path_endpoints TINYINT NOT NULL DEFAULT 1,
|
||||
end_distance FLOAT NOT NULL DEFAULT 25,
|
||||
split_long_min FLOAT NOT NULL DEFAULT 300,
|
||||
split_long_step FLOAT NOT NULL DEFAULT 200,
|
||||
|
||||
#node combining settings:
|
||||
same_dist FLOAT NOT NULL DEFAULT 2.5,
|
||||
node_combine_dist FLOAT NOT NULL DEFAULT 30,
|
||||
grid_combine_dist FLOAT NOT NULL DEFAULT 30,
|
||||
close_all_los TINYINT NOT NULL DEFAULT 0,
|
||||
|
||||
#line-crossing reduction settings:
|
||||
cross_count INT NOT NULL DEFAULT 5,
|
||||
cross_min_length FLOAT NOT NULL DEFAULT 1,
|
||||
cross_max_z_diff FLOAT NOT NULL DEFAULT 20,
|
||||
cross_combine_dist FLOAT NOT NULL DEFAULT 120,
|
||||
|
||||
#linking:
|
||||
second_link_dist FLOAT NOT NULL DEFAULT 100,
|
||||
link_max_dist FLOAT NOT NULL DEFAULT 400,
|
||||
link_count TINYINT NOT NULL DEFAULT 1
|
||||
|
||||
);
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#include "../common/types.h"
|
||||
#include "../zone/map.h"
|
||||
#include "../common/rdtsc.h"
|
||||
#include "quadtree.h"
|
||||
#include "apathing.h"
|
||||
#include "boostcrap.h"
|
||||
#include <stdio.h>
|
||||
#include <mysql.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <gd.h>
|
||||
|
||||
|
||||
|
||||
|
||||
//parameters:
|
||||
bool INCLUDE_DOORS = true;
|
||||
float FEAR_MAXIMUM_DISTANCE = 250;
|
||||
float ENDPOINT_CONNECT_MAX_DISTANCE = 25;
|
||||
float MIN_FIX_Z = 20.0f;
|
||||
float CLOSE_ENOUGH = 2.5;
|
||||
bool COMBINE_CHECK_ALL_LOS = false;
|
||||
float CLOSE_ENOUGH_COMBINE = 30; //30;
|
||||
float SPAWN_MIN_SECOND_DIST = 100;
|
||||
bool SPLIT_INVALID_PATHS = false;
|
||||
bool LINK_PATH_ENDPOINTS = true;
|
||||
float MAX_LINK_SPAWN_DIST = 400;
|
||||
float FINAL_LINK_POINTS_DIST = 30;
|
||||
bool SPAWN_LINK_TWICE = true;
|
||||
bool SPAWN_LINK_THRICE = true;
|
||||
float MERGE_MIN_SECOND_DIST = 30;
|
||||
float SPLIT_LINE_LENGTH = 300;
|
||||
float SPLIT_LINE_INTERVAL = 200;
|
||||
float LONG_PATH_CHECK_LOS = 0; //0=disable
|
||||
int CROSS_REDUCE_COUNT = 5;
|
||||
float CROSS_MIN_LENGTH = 1;
|
||||
float CROSS_MAX_Z_DIFF = 20;
|
||||
float CLOSE_ENOUGH_CROSS = 120;
|
||||
int IMAGE_SCALE = 4;
|
||||
|
||||
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
srand(2038833498);
|
||||
|
||||
/* const char *zone =
|
||||
//"qeynos";
|
||||
"qeynos2";
|
||||
//"northkarana";
|
||||
*/
|
||||
if(argc != 2) {
|
||||
printf("Usage: %s [zone_short_name]\n", argv[0]);
|
||||
return(1);
|
||||
}
|
||||
const char *zone = argv[1];
|
||||
char buf[256];
|
||||
|
||||
MYSQL m;
|
||||
|
||||
list<PathGraph *> db_paths;
|
||||
list<PathNode *> db_spawns;
|
||||
|
||||
mysql_init(&m);
|
||||
|
||||
if(!mysql_real_connect(&m, DB_HOST, DB_LOGIN, DB_PASSWORD, DB_NAME, 0, NULL, 0)) {
|
||||
printf("Unable to connect: %s.\n", mysql_error(&m));
|
||||
return(1);
|
||||
}
|
||||
|
||||
/*
|
||||
Data loading phase
|
||||
*/
|
||||
|
||||
//load up our map file
|
||||
Map *map = Map::LoadMapfile(zone);
|
||||
if(map == NULL) {
|
||||
printf("Unable to load map file.");
|
||||
return(1);
|
||||
}
|
||||
|
||||
//try to load the EQ map file to make our pictures prettier
|
||||
PathGraph eqmap;
|
||||
if(load_eq_map(zone, &eqmap)) {
|
||||
printf("Loaded EQ Client map: %d edges.\n", eqmap.edges.size());
|
||||
} else {
|
||||
printf("Unable to load EQ Client map, continuing without it.\n");
|
||||
}
|
||||
|
||||
//load our crap from the DB...
|
||||
if(!load_paths_from_db(&m, map, zone, db_paths, db_spawns))
|
||||
return(1);
|
||||
|
||||
if(db_paths.size() == 0)
|
||||
db_paths.push_back(new PathGraph());
|
||||
|
||||
if(!load_spawns_from_db(&m, zone, db_spawns))
|
||||
return(1);
|
||||
|
||||
if(INCLUDE_DOORS) {
|
||||
if(!load_doors_from_db(&m, zone, db_spawns))
|
||||
return(1);
|
||||
}
|
||||
|
||||
if(!load_hints_from_db(&m, zone, db_spawns))
|
||||
return(1);
|
||||
|
||||
|
||||
/*{
|
||||
PathGraph *g;
|
||||
PathNode *cur = NULL, *last = NULL;
|
||||
g = new PathGraph();
|
||||
g->nodes.push_back(cur = new PathNode(200, 200, 5));
|
||||
last = cur;
|
||||
g->nodes.push_back(cur = new PathNode(GPoint(700, 200, 5)));
|
||||
g->add_edge(last, cur); last = cur;
|
||||
g->nodes.push_back(cur = new PathNode(GPoint(700, -300, 5)));
|
||||
g->add_edge(last, cur); last = cur;
|
||||
g->nodes.push_back(cur = new PathNode(GPoint(200, -300, 5)));
|
||||
g->add_edge(last, cur); last = cur;
|
||||
g->nodes.push_back(cur = new PathNode(GPoint(200, 200, 5)));
|
||||
g->add_edge(last, cur); last = cur;
|
||||
db_paths.push_back(g);
|
||||
}*/
|
||||
/*{
|
||||
PathGraph *g;
|
||||
PathNode *cur = NULL, *last = NULL;
|
||||
g = new PathGraph();
|
||||
g->nodes.push_back(cur = new PathNode(200, 200, 5));
|
||||
last = cur;
|
||||
g->nodes.push_back(cur = new PathNode(GPoint(385, 35, 23)));
|
||||
g->add_edge(last, cur); last = cur;
|
||||
g->nodes.push_back(cur = new PathNode(GPoint(450, -50, 23)));
|
||||
g->add_edge(last, cur); last = cur;
|
||||
g->nodes.push_back(cur = new PathNode(GPoint(535, -115, 23)));
|
||||
g->add_edge(last, cur); last = cur;
|
||||
g->nodes.push_back(cur = new PathNode(GPoint(700, -300, 5)));
|
||||
g->add_edge(last, cur); last = cur;
|
||||
db_paths.push_back(g);
|
||||
|
||||
g = new PathGraph();
|
||||
g->nodes.push_back(cur = new PathNode(700, 200, 5));
|
||||
last = cur;
|
||||
g->nodes.push_back(cur = new PathNode(GPoint(535, 35, 23)));
|
||||
g->add_edge(last, cur); last = cur;
|
||||
g->nodes.push_back(cur = new PathNode(GPoint(450, -50, 23)));
|
||||
g->add_edge(last, cur); last = cur;
|
||||
g->nodes.push_back(cur = new PathNode(GPoint(385, -115, 23)));
|
||||
g->add_edge(last, cur); last = cur;
|
||||
g->nodes.push_back(cur = new PathNode(GPoint(200, -300, 5)));
|
||||
g->add_edge(last, cur); last = cur;
|
||||
db_paths.push_back(g);
|
||||
}*/
|
||||
|
||||
|
||||
//try to load settings, dont care if it fails
|
||||
if(load_settings_from_db(&m, zone))
|
||||
printf("Loaded zone settings from the database.\n");
|
||||
else
|
||||
printf("Unable to load settings from database. Using defaults.\n");
|
||||
|
||||
|
||||
printf("Load: got %d paths and %d spawn points from the database.\n", db_paths.size(), db_spawns.size());
|
||||
printf("Load: had to split up %d invalid paths.\n", load_split_paths);
|
||||
|
||||
/*
|
||||
The make-the-db-suck-less phase
|
||||
*/
|
||||
|
||||
//try to lower waypoints way in the sky:
|
||||
repair_high_waypoints(map, db_paths, db_spawns);
|
||||
printf("Fix Z: %d missed map, %d were not broken, %d were fixed.\n", z_no_map_count, z_not_fixed_count, z_fixed_count);
|
||||
printf("Fix Z: broken avg diff=%.3f, not broken avg diff=%.3f\n", z_fixed_diffs/z_fixed_count, z_not_fixed_diffs/z_not_fixed_count);
|
||||
|
||||
//run a waypoint reduction algorithm in 3space:
|
||||
reduce_waypoints(db_paths);
|
||||
printf("WP Reduce: removed %d redundant waypoints.\n", wp_reduce_count);
|
||||
|
||||
/*
|
||||
Graph connection and merging phase
|
||||
*/
|
||||
//make trivial connections of nodes at about the same spot on diff grids
|
||||
combine_trivial_grids(map, db_paths);
|
||||
printf("Trivial Merge: %d grids merged.\n", trivial_merge_count);
|
||||
|
||||
//now do the 'closest with LOS' connection method
|
||||
combine_closest_grids(map, db_paths);
|
||||
printf("Closest Merge: %d grids linked, %d grids double-linked.\n", closest_merge_count, closest_merge2_count);
|
||||
PathGraph *big = db_paths.front();
|
||||
|
||||
//now add in the spawn points, and link to closest with LOS
|
||||
link_spawns(map, big, db_spawns, MAX_LINK_SPAWN_DIST, NULL);
|
||||
printf("Link Spawns: %d linked once, %d linked twice, %d not linked, %d invalid.\n", link_spawn_count, link_spawn2_count, link_spawn_nocount, link_spawn_invalid);
|
||||
|
||||
//combining close points might be causing small LOS obstacles...
|
||||
//so we want to run this before we combine them.
|
||||
//this seems to do more harm than good right now
|
||||
// check_edge_los(map, big);
|
||||
// printf("Bad Edges: removed %d no-LOS edges.\n", removed_edges_los);
|
||||
|
||||
#ifdef LONG_PATH_CHECK_LOS
|
||||
//check long paths LOS, we seem to have a problem with random long links
|
||||
//disable this if we check all edges above...
|
||||
check_long_edge_los(map, big);
|
||||
printf("Bad Edges: removed %d long no-LOS edges.\n", removed_long_edges_los);
|
||||
#endif
|
||||
|
||||
//clean up points close enough to eachother to be the same point.
|
||||
combine_grid_points(map, big, CLOSE_ENOUGH_COMBINE);
|
||||
printf("Point Combine: combined %d very close nodes (%d missed strict LOS).\n", combined_grid_points, combine_broke_los);
|
||||
printf("Point Combine: so far, %d LOS cache hits, %d LOS cache misses.\n", los_cache_hits, los_cache_misses);
|
||||
|
||||
list<PathEdge *> all_edges = big->edges;
|
||||
printf("Big Graph: %d original nodes, %d original edges.\n", big->nodes.size(), big->edges.size());
|
||||
|
||||
#ifdef DRAW_PRETREE_GRAPH
|
||||
//draw out our graph before trimming
|
||||
sprintf(buf, "paths-%s-pretree.png", zone);
|
||||
draw_paths(map, big->edges, eqmap.edges, buf);
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
Graph algorithm application (boost)
|
||||
run it on each disjoint graph
|
||||
*/
|
||||
|
||||
vector<PathGraph *> disjoints;
|
||||
vector<int> start_nodes;
|
||||
|
||||
//find all the disjoint graphs
|
||||
{
|
||||
//build the boost graph
|
||||
MyGraph boost_graph(big->nodes.size());
|
||||
property_map<MyGraph, edge_weight_t>::type weightlist;
|
||||
std::map<PathEdge *, EdgeDesc> edgemap;
|
||||
build_boost_graph(boost_graph, weightlist, edgemap, big, false);
|
||||
|
||||
//find the grid which has most of the edges
|
||||
sprintf(buf, "paths-%s-colors.png", zone);
|
||||
find_disjoint_grids(map, boost_graph, big, buf, start_nodes, disjoints);
|
||||
}
|
||||
printf("\nSplit: There are %d valid disjoint graphs.\n", disjoints.size());
|
||||
|
||||
//for each disjoint graph....
|
||||
int djnum = 0;
|
||||
PathGraph *tmpg; int start_node;
|
||||
vector<PathGraph *>::iterator cur,end;
|
||||
vector<int>::iterator curs,ends;
|
||||
cur = disjoints.begin(); curs = start_nodes.begin();
|
||||
end = disjoints.end(); ends = start_nodes.end();
|
||||
for(; cur != end; cur++,curs++) {
|
||||
big = *cur;
|
||||
start_node = *curs;
|
||||
|
||||
printf("Disjoint %d: has %d edges and %d nodes.\n", djnum, big->edges.size(), big->nodes.size());
|
||||
|
||||
//reset our stats...
|
||||
combine_broke_los = 0;
|
||||
combined_grid_points = 0;
|
||||
removed_edges_los = 0;
|
||||
removed_long_edges_los = 0;
|
||||
broke_paths = 0;
|
||||
cross_edge_count = 0;
|
||||
cross_add_count = 0;
|
||||
|
||||
{
|
||||
//build the boost graph
|
||||
MyGraph boost_graph(big->nodes.size());
|
||||
property_map<MyGraph, edge_weight_t>::type weightlist;
|
||||
std::map<PathEdge *, EdgeDesc> edgemap;
|
||||
build_boost_graph(boost_graph, weightlist, edgemap, big);
|
||||
|
||||
//calculate the MST
|
||||
run_min_spanning_tree(boost_graph, weightlist, edgemap, big, start_node);
|
||||
printf("Ran Min Spanning Tree: ended with %d edges\n", big->edges.size());
|
||||
}
|
||||
|
||||
/*
|
||||
Now we have our minimal spanning tree, try to refine it.
|
||||
|
||||
the goal of this crap is to fix newbie fields and open zones
|
||||
*/
|
||||
std::map<PathEdge*, vector<GPoint> > cross_list;
|
||||
PathGraph *cross_big = new PathGraph();
|
||||
PathGraph *cross_excess = new PathGraph();
|
||||
|
||||
//count the number of times each edge crosses another edge, and record
|
||||
//the intersection points. Also seperate crossers from non-crossers
|
||||
count_crossing_lines(big->edges, cross_big, cross_excess, cross_list);
|
||||
printf("Cross Count: %d edges cross more than the specified number of other edges.\n", cross_edge_count);
|
||||
|
||||
if(cross_edge_count > 2) {
|
||||
//Make waypoints at all points of intersection
|
||||
cut_crossed_grids(cross_big, cross_list);
|
||||
printf("Cross Cut: Created %d new nodes cutting intersections\n", cross_add_count);
|
||||
|
||||
//combine close points with a somewhat big radius...
|
||||
combine_grid_points(map, cross_big, CLOSE_ENOUGH_CROSS);
|
||||
printf("Cross Combine: combined %d nodes. (%d missed strict LOS)\n", combined_grid_points, combine_broke_los);
|
||||
printf("Cross Combine: so far, %d LOS cache hits, %d LOS cache misses.\n", los_cache_hits, los_cache_misses);
|
||||
|
||||
//build our boost graph, so we can do reachability
|
||||
MyGraph cross_graph(cross_big->nodes.size());
|
||||
property_map<MyGraph, edge_weight_t>::type cross_weightlist;
|
||||
std::map<PathEdge *, EdgeDesc> cross_edgemap;
|
||||
build_boost_graph(cross_graph, cross_weightlist, cross_edgemap, cross_big, false);
|
||||
|
||||
//isolate each disjoint graph and try to reduce it, gathering
|
||||
//all non-cross points and edges while we are at it.
|
||||
sprintf(buf, "paths-%s-crosses.png", zone);
|
||||
consolidate_cross_graphs(map, cross_big, cross_excess, cross_graph, buf);
|
||||
|
||||
//rebuild the big graph by merging cross_big and cross_excess
|
||||
//might be as simple as append the two arrays and run a combine on it.
|
||||
//leaks 'big'
|
||||
*cur = big = cross_excess;
|
||||
cross_excess->add_edges(cross_big->edges);
|
||||
rebuild_node_list(big->edges, big->nodes);
|
||||
|
||||
//This is used to re-link the cross grids with the non-cross stuff
|
||||
combine_grid_points(map, big, CLOSE_ENOUGH_COMBINE);
|
||||
printf("Cross Merge: combined %d close nodes. (%d missed strict LOS)\n", combined_grid_points, combine_broke_los);
|
||||
printf("Cross Merge: so far, %d LOS cache hits, %d LOS cache misses.\n", los_cache_hits, los_cache_misses);
|
||||
|
||||
//build yet another boost graph so we can run MST
|
||||
MyGraph cross_graph_final(big->nodes.size());
|
||||
property_map<MyGraph, edge_weight_t>::type cross_weightlist_final;
|
||||
std::map<PathEdge *, EdgeDesc> cross_edgemap_final;
|
||||
build_boost_graph(cross_graph_final, cross_weightlist_final, cross_edgemap_final, big);
|
||||
|
||||
//run our MST to reduce the new cross-reduced graph
|
||||
run_min_spanning_tree(cross_graph_final, cross_weightlist_final, cross_edgemap_final, big, 0);
|
||||
printf("Ran Min Spanning Tree 2: ended with %d edges\n", big->edges.size());
|
||||
} //end if there were some cross edges
|
||||
|
||||
/*
|
||||
Final refinement phase, the graph has been reduced to our final
|
||||
form, this phase is for adding anything back in we might want
|
||||
*/
|
||||
|
||||
//now that we reduced all our co-linear points, add a bunch back in for
|
||||
//long paths, so we have more points over space. Idea is that this way
|
||||
//we control how many colinear points there are, it isnt random
|
||||
//this counteracts any attempts to use line-crossing as a criteria for reduction
|
||||
//for some stupid reason, this just destroys the graph
|
||||
// break_long_lines(db_paths);
|
||||
// printf("WP Increase: created %d waypoints on long paths.\n", broke_paths);
|
||||
|
||||
|
||||
/*
|
||||
Things we might want to do:
|
||||
- Try to create some cycles. Specifically large cycles.
|
||||
- do this after reachability calculations
|
||||
- use allready calculated reacahbility and distances, just adjust them as cycles added
|
||||
- these will add more realism to the pathing
|
||||
- might be implemented like this:
|
||||
- run all pairs shortest path on the MST (is this a byproduct?)
|
||||
- for each node N, for each other node K
|
||||
- if path(N, K) is at least MIN_CYCLE_JOIN_PATH (to prevent making small cycles)
|
||||
- and dist(N,K) is less than MAX_CYCLE_JOIN_DIST (do not want to invent long paths)
|
||||
- and there is LOS from N to K
|
||||
- connect N and K
|
||||
- have to re-run all pairs again... that sucks
|
||||
- another twist on the implementation would be to only look at paths
|
||||
which were discarded by the MST. Or maybe run this first, then the other.
|
||||
*/
|
||||
|
||||
/*
|
||||
The final tree has been built, remove anything we dont need from
|
||||
it, and gather some information.
|
||||
*/
|
||||
|
||||
//build a boost graph out of our minimal spanning tree.
|
||||
MyGraph boost_mst(big->nodes.size());
|
||||
property_map<MyGraph, edge_weight_t>::type weightlist_mst;
|
||||
std::map<PathEdge *, EdgeDesc> edgemap_mst;
|
||||
build_boost_graph(boost_mst, weightlist_mst, edgemap_mst, big, true);
|
||||
|
||||
//determine the path lengths to all nodes from all others
|
||||
//including the longest path reachable by each node.
|
||||
//this also cleans up big by removing anything unreachable
|
||||
// vector< vector<int> > AllPairs;
|
||||
sprintf(buf, "paths-%s-mstcolors%d.png", zone, djnum++);
|
||||
// calc_path_lengths(map, boost_mst, big, AllPairs, buf);
|
||||
calc_path_lengths(map, boost_mst, big, edgemap_mst, buf);
|
||||
printf("Calculated the longest paths from each node.\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
printf("Combining all disjoint graphs...\n");
|
||||
//now combine all our disjoint graphs into one big one...
|
||||
big = new PathGraph();
|
||||
cur = disjoints.begin();
|
||||
end = disjoints.end();
|
||||
big->nodes.clear();
|
||||
big->edges.clear();
|
||||
djnum = 1;
|
||||
for(; cur != end; cur++, djnum++) {
|
||||
tmpg = *cur;
|
||||
list<PathEdge *>::iterator cure,ende;
|
||||
cure = tmpg->edges.begin();
|
||||
ende = tmpg->edges.end();
|
||||
for(; cure != ende; cure++) {
|
||||
big->edges.push_back(*cure);
|
||||
}
|
||||
// sprintf(buf, "paths-%s-dj%d.png", zone, djnum-1);
|
||||
// just_color_the_damned_thing(map, tmpg, buf);
|
||||
}
|
||||
rebuild_node_list(big->edges, big->nodes, NULL);
|
||||
|
||||
printf("Validating results...\n");
|
||||
validate_edges(map, big);
|
||||
|
||||
//now we have our final node and edge set.
|
||||
//build a graph of all final nodes to find pathing info
|
||||
// MyGraph final(big->nodes.size());
|
||||
// property_map<MyGraph, edge_weight_t>::type weightlist_final;
|
||||
// std::map<PathEdge *, EdgeDesc> edgemap_final;
|
||||
// build_boost_graph(final, weightlist_final, edgemap_final, big, true);
|
||||
|
||||
// sprintf(buf, "paths-%s-mstcolors%d-ppi.png", zone, djnum-1);
|
||||
// just_color_the_damned_thing(map, big, buf);
|
||||
|
||||
vector< vector<PathEdge*> > path_finding;
|
||||
find_path_info(map, big, path_finding);
|
||||
printf("Calculated pathing information...\n");
|
||||
|
||||
//write out a nice image of our MST
|
||||
sprintf(buf, "paths-%s-mstree.png", zone);
|
||||
draw_paths2(map, all_edges, big->edges, eqmap.edges, db_spawns, buf);
|
||||
|
||||
//write out a map for inside the EQ client, not as pretty as the PNG
|
||||
sprintf(buf, "eqmaps_out/%s_2.txt", zone);
|
||||
write_eq_map(big->edges, buf);
|
||||
|
||||
/*
|
||||
Build structures, and write out the path file.
|
||||
*/
|
||||
RDTSC_Timer t1, t2;
|
||||
QTNode *root;
|
||||
root = build_quadtree(map, big);
|
||||
if(root == NULL) {
|
||||
printf("Failed to build quadtree, quitting.\n");
|
||||
return(1);
|
||||
}
|
||||
|
||||
t2.start();
|
||||
sprintf(buf, "%s.path", zone);
|
||||
if(!write_path_file(root, big, buf, path_finding)) {
|
||||
printf("Unable to write path file.\n");
|
||||
return(1);
|
||||
}
|
||||
|
||||
printf("Everything completed successfully. %s.path has been generated.\n", zone);
|
||||
|
||||
/*
|
||||
|
||||
** now we have a minimal connected graph such that any mob can get anywhere
|
||||
by only a single path.
|
||||
|
||||
|
||||
|
||||
** now we have our final pathing grid. determine some useful info for each node.
|
||||
|
||||
look for dead ends/node preference:
|
||||
- at each node, sum up the length of all edges reachable by using that link
|
||||
- for each node as N
|
||||
-- for each edge leaving N as E
|
||||
---- reset marks on all edges
|
||||
---- mark all edges leaving N
|
||||
---- perform a depth first search of all reachable nodes
|
||||
------ Only traverse unmarked edges. Mark each edge as it is traversed.
|
||||
---- unmark all edges leaving N, except E
|
||||
---- sum the length of all marked edges
|
||||
---- store this reachable length as a weight for this edge of this node.
|
||||
-- for each edge leaving N as E
|
||||
---- determine highest edge weight in this node
|
||||
-- for each edge leaving N as E
|
||||
---- count number of edges within RANDOM_WALK % of the highest node.
|
||||
-- sort the edge list for this node to have all random nodes first
|
||||
-- store the random walkable counter for this node
|
||||
|
||||
|
||||
|
||||
** Finally we have all the calculation
|
||||
|
||||
things we need to store:
|
||||
nodes
|
||||
edge lists
|
||||
quadtree containing nodes
|
||||
|
||||
- We do not really need to store edges which are not on the random walk
|
||||
list since the pathing code will never consider using them.
|
||||
|
||||
Node {
|
||||
x, y, z
|
||||
edge list pointer
|
||||
edge list length
|
||||
random walkable counter
|
||||
}
|
||||
|
||||
edge list entry {
|
||||
ending node pointer
|
||||
reachable edge weight? (not used by pathing, might have other purpose)
|
||||
}
|
||||
|
||||
quadtree node {
|
||||
minx, maxx, miny, maxy
|
||||
union {
|
||||
node pointers: q1, q2, q3, q4
|
||||
node list offset and length
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
how to do fear pathing...
|
||||
when a mob is feared, it uses the quadtree to find the node closest to it
|
||||
that it can see to. The LOS requirement will make this more difficult, but feasible.
|
||||
|
||||
Once it has found its first node, the mob will set this as its waypoint and go there.
|
||||
|
||||
Once the mob reaches a node, it will look at the node's random counter (RC)
|
||||
if RC is 1, take the first edge and use its terminal as next waypoint
|
||||
if RC is >1, randomly choose an edge from 0 to RC-1, and walk that edge.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
mysql_close(&m);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,290 @@
|
||||
#ifndef APATHING_H
|
||||
#define APATHING_H
|
||||
|
||||
#include <mysql.h>
|
||||
#include <gd.h>
|
||||
|
||||
#define DB_HOST "10.1.1.1"
|
||||
#define DB_LOGIN "eqserver"
|
||||
#define DB_PASSWORD "pw4eqserver"
|
||||
#define DB_NAME "peq"
|
||||
|
||||
//for now, all of these were arbitrarily chosen
|
||||
|
||||
//load up doors as spawn points, since they are also valid locations
|
||||
extern bool INCLUDE_DOORS;
|
||||
|
||||
//this is the furthest a mob can be from a fear point and still expect
|
||||
//to find the node.
|
||||
extern float FEAR_MAXIMUM_DISTANCE;
|
||||
|
||||
extern float ENDPOINT_CONNECT_MAX_DISTANCE; //begin and end point must be this close
|
||||
extern float MIN_FIX_Z; //minimum drop before correcting waypoint
|
||||
|
||||
#define ALMOST_COLINEAR_COS 0.99f //cosine of an angle to consider colinear... .99== 8 degrees
|
||||
|
||||
//if we miss the map on one Z-checking try, move the point by these
|
||||
#define X_JITTER 3
|
||||
#define Y_JITTER 3
|
||||
|
||||
//if two nodes are this close together, consider them the same
|
||||
extern float CLOSE_ENOUGH;
|
||||
|
||||
//this causes us to check all of a node's edges for LOS from the new
|
||||
//node when we are considering combining into that node
|
||||
//this is not working as well as one might hope, leads to many invalids
|
||||
extern bool COMBINE_CHECK_ALL_LOS;
|
||||
|
||||
//this is bigger than close enough, since we check LOS when combining these
|
||||
//so that we prevent little juntions
|
||||
extern float CLOSE_ENOUGH_COMBINE;
|
||||
|
||||
//a second link on a spawn must be this far away from the first.
|
||||
extern float SPAWN_MIN_SECOND_DIST;
|
||||
|
||||
//uncomment to split a pathin with two waypoints which cannot see eachother into two
|
||||
extern bool SPLIT_INVALID_PATHS;
|
||||
|
||||
//enabled linking of path endpoints as if they were spawn points.
|
||||
extern bool LINK_PATH_ENDPOINTS;
|
||||
|
||||
//the maximum distance of the closest point to a spawn inorder to link it
|
||||
extern float MAX_LINK_SPAWN_DIST;
|
||||
|
||||
//before we generate pathing info, we try to link all points within this range to eachother
|
||||
extern float FINAL_LINK_POINTS_DIST;
|
||||
|
||||
//enables linking a spawn point to two nodes instead of just one.
|
||||
extern bool SPAWN_LINK_TWICE;
|
||||
extern bool SPAWN_LINK_THRICE; //link up to 3 times.. requires twice enabled
|
||||
|
||||
//a second link point must be further than this from the first for closest merge
|
||||
extern float MERGE_MIN_SECOND_DIST;
|
||||
|
||||
//if an edge is longer than this, it will get cut into pieces interval long
|
||||
extern float SPLIT_LINE_LENGTH;
|
||||
extern float SPLIT_LINE_INTERVAL;
|
||||
|
||||
//an edge must cross this many lines before being considered for cross reduction
|
||||
extern int CROSS_REDUCE_COUNT;
|
||||
//an edge must be longer than this before we think it can cross anything
|
||||
extern float CROSS_MIN_LENGTH;
|
||||
//The intersect points of two edges must be within this range of Z to count
|
||||
extern float CROSS_MAX_Z_DIFF;
|
||||
//the max distance between two nodes to consider them the same when crossing
|
||||
extern float CLOSE_ENOUGH_CROSS;
|
||||
|
||||
//check long paths on load for LOS
|
||||
//#define LONG_PATH_CHECK_LOS
|
||||
|
||||
//minimum number of nodes for a graph to possible be a disjoint graph
|
||||
#define MIN_DISJOINT_NODES 3 //5
|
||||
|
||||
//divide the image scale by this number in each direction
|
||||
extern int IMAGE_SCALE;
|
||||
|
||||
//enable drawing of a color-by-reachability graph when coloring a graph
|
||||
#define DRAW_ALL_COLORS 1
|
||||
|
||||
//enable drawing of the original non-reduced combined graph.
|
||||
#define DRAW_PRETREE_GRAPH 1
|
||||
|
||||
#include "gpoint.h"
|
||||
#include <vector>
|
||||
#include <list>
|
||||
using namespace std;
|
||||
|
||||
class PathNode : public GPoint {
|
||||
public:
|
||||
PathNode() {
|
||||
init();
|
||||
}
|
||||
PathNode(const GPoint &them) : GPoint(them) {
|
||||
init();
|
||||
}
|
||||
PathNode(float ix, float iy, float iz) : GPoint(ix, iy, iz) {
|
||||
init();
|
||||
}
|
||||
void init() {
|
||||
color = 0;
|
||||
// color2 = 0;
|
||||
final_id = -1;
|
||||
longest_path = 0;
|
||||
valid = true;
|
||||
forced = false;
|
||||
disjoint = false;
|
||||
}
|
||||
|
||||
int node_id;
|
||||
int final_id;
|
||||
// inherited:
|
||||
// float x;
|
||||
// float y;
|
||||
// float z;
|
||||
// VertDesc vert;
|
||||
|
||||
int color;
|
||||
int longest_path;
|
||||
|
||||
char valid:1,
|
||||
forced:1,
|
||||
disjoint:1,
|
||||
extra:5;
|
||||
|
||||
float Dist2(const GPoint *o) const {
|
||||
float tmp;
|
||||
float sum;
|
||||
tmp = x - o->x;
|
||||
sum = tmp*tmp;
|
||||
tmp = y - o->y;
|
||||
sum += tmp*tmp;
|
||||
tmp = z - o->z;
|
||||
sum += tmp*tmp;
|
||||
return(sum);
|
||||
}
|
||||
};
|
||||
|
||||
class PathEdge {
|
||||
public:
|
||||
PathEdge( PathNode *_from, PathNode *_to) {
|
||||
from = _from;
|
||||
to = _to;
|
||||
normal_reach = -1;
|
||||
reverse_reach = -1;
|
||||
valid = true;
|
||||
}
|
||||
PathNode *from;
|
||||
PathNode *to;
|
||||
|
||||
int normal_reach;
|
||||
int reverse_reach;
|
||||
bool valid;
|
||||
// EdgeDesc edge_id;
|
||||
};
|
||||
|
||||
class PathGraph {
|
||||
public:
|
||||
~PathGraph() {
|
||||
{
|
||||
list<PathNode *>::iterator cur,end;
|
||||
cur = nodes.begin();
|
||||
end = nodes.end();
|
||||
for(; cur != end; cur++) {
|
||||
delete *cur;
|
||||
}
|
||||
}
|
||||
{
|
||||
list<PathEdge *>::iterator cur,end;
|
||||
cur = edges.begin();
|
||||
end = edges.end();
|
||||
for(; cur != end; cur++) {
|
||||
delete *cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add_edge(PathNode *b, PathNode *e) {
|
||||
edges.push_back(new PathEdge(b, e));
|
||||
}
|
||||
|
||||
void add_edges(list<PathEdge *> &o) {
|
||||
list<PathEdge *>::iterator cur,end;
|
||||
cur = o.begin();
|
||||
end = o.end();
|
||||
for(; cur != end; cur++) {
|
||||
edges.push_back(*cur);
|
||||
}
|
||||
}
|
||||
|
||||
list<PathNode *> nodes;
|
||||
list<PathEdge *> edges;
|
||||
|
||||
//used for graph color accounts
|
||||
int curcolor;
|
||||
int ccount;
|
||||
};
|
||||
|
||||
|
||||
class ColorRecord {
|
||||
public:
|
||||
int color;
|
||||
float height;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
extern int load_split_paths;
|
||||
extern int z_fixed_count;
|
||||
extern int z_not_fixed_count;
|
||||
extern int z_no_map_count;
|
||||
extern float z_fixed_diffs;
|
||||
extern float z_not_fixed_diffs;
|
||||
extern int wp_reduce_count;
|
||||
extern int trivial_merge_count;
|
||||
extern int closest_merge_count;
|
||||
extern int closest_merge2_count;
|
||||
extern int link_spawn_count;
|
||||
extern int link_spawn_invalid;
|
||||
extern int link_spawn2_count;
|
||||
extern int link_spawn3_count;
|
||||
extern int link_spawn_nocount;
|
||||
extern int combine_broke_los;
|
||||
extern int combined_grid_points;
|
||||
extern int removed_edges_los;
|
||||
extern int removed_long_edges_los;
|
||||
extern int broke_paths;
|
||||
extern int cross_edge_count;
|
||||
extern int cross_add_count;
|
||||
extern int los_cache_misses;
|
||||
extern int los_cache_hits;
|
||||
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
using namespace std;
|
||||
|
||||
class QTNode;
|
||||
|
||||
//ye-olde prototypes
|
||||
bool load_paths_from_db(MYSQL *m, Map *map, const char *zone, list<PathGraph*> &db_paths, list<PathNode*> &end_points);
|
||||
bool load_spawns_from_db(MYSQL *m, const char *zone, list<PathNode*> &db_spawns);
|
||||
bool load_doors_from_db(MYSQL *m, const char *zone, list<PathNode*> &db_spawns);
|
||||
bool load_hints_from_db(MYSQL *m, const char *zone, list<PathNode*> &db_spawns);
|
||||
bool load_settings_from_db(MYSQL *m, const char *zone);
|
||||
void repair_a_high_waypoint(Map *map, PathNode *it);
|
||||
void repair_high_waypoints(Map *map, list<PathGraph*> &db_paths, list<PathNode*> &db_spawns);
|
||||
bool almost_colinear(PathNode *first, PathNode *second, PathNode *third);
|
||||
void reduce_waypoints(list<PathGraph*> &db_paths);
|
||||
void break_long_lines(list<PathGraph*> &db_paths);
|
||||
//void build_big_graph(PathGraph *big, list<PathGraph*> &db_paths, list<PathNode*> &db_spawns);
|
||||
void combine_trivial_grids(Map *map, list<PathGraph*> &db_paths);
|
||||
void combine_closest_grids(Map *map, list<PathGraph*> &db_paths);
|
||||
void link_spawns(Map *map, PathGraph *big, list<PathNode*> &db_spawns, float maxdist, map< pair<PathNode *, PathNode *>, bool > *edgelist);
|
||||
void combine_grid_points(Map *map, PathGraph *big, float close_enough);
|
||||
void draw_paths(Map *map, list<PathEdge *> &edges, list<PathEdge *> &edges2, const char *fname);
|
||||
void draw_paths2(Map *map, list<PathEdge *> &edges1, list<PathEdge *> &edges2, list<PathEdge *> &edges3, list<PathNode *> &spawns, const char *fname);
|
||||
void check_edge_los(Map *map, PathGraph *big);
|
||||
void check_long_edge_los(Map *map, PathGraph *big);
|
||||
bool CheckLOS(Map *map, PathNode *from, PathNode *to);
|
||||
void cut_crossed_grids(PathGraph *big, map<PathEdge*, vector<GPoint> > &cross_list);
|
||||
void rebuild_node_list(list<PathEdge *> &edges, list<PathNode *> &nodes, list<PathNode *> *excess_nodes = NULL);
|
||||
QTNode *build_quadtree(Map *map, PathGraph *big);
|
||||
bool write_path_file(QTNode *_root, PathGraph *big, const char *file, vector< vector<PathEdge*> > &path_finding);
|
||||
bool load_eq_map(const char *zone, PathGraph *eqmap);
|
||||
void write_eq_map(list<PathEdge *> &edges, const char *fname);
|
||||
//void edge_stats(list<PathEdge*> &edges, const char *s);
|
||||
void choose_biggest_graph(PathGraph *big, vector<int> &counts, vector<int> &first_node);
|
||||
void find_path_info(Map *map, PathGraph *big, vector< vector<PathEdge *> > &path_finding);
|
||||
void find_node_edges(PathGraph *big, std::map<PathNode*, vector<PathEdge*> > &node_edges);
|
||||
void validate_edges(Map *map, PathGraph *big);
|
||||
|
||||
void DrawGradientLine(gdImagePtr im, GPoint *first, GPoint *second, vector<ColorRecord> &colors);
|
||||
void allocateGradient(gdImagePtr im, float r1, float g1, float b1, float r2, float g2, float b2,
|
||||
float min, float max, float divs, vector<ColorRecord> &colors);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,759 @@
|
||||
/*
|
||||
|
||||
Fear Pathing generation utility.
|
||||
(c) 2005 Father Nitwit
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#include "../common/types.h"
|
||||
#include "../zone/map.h"
|
||||
#include "../common/rdtsc.h"
|
||||
#include "quadtree.h"
|
||||
#include "apathing.h"
|
||||
#include "boostcrap.h"
|
||||
#include <stdio.h>
|
||||
#include <mysql.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <gd.h>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
We assume this overwrites the edge array in big
|
||||
and does not free the old edges
|
||||
*/
|
||||
void build_boost_graph(MyGraph &vg, property_map<MyGraph, edge_weight_t>::type &weightmap, map<PathEdge *, EdgeDesc> &em, PathGraph *big, bool set_weights) {
|
||||
|
||||
list<PathEdge*>::iterator cur,end;
|
||||
list<PathNode*>::iterator curn,endn;
|
||||
PathNode *n;
|
||||
PathEdge *e;
|
||||
|
||||
//first we need to number our nodes...
|
||||
curn = big->nodes.begin();
|
||||
endn = big->nodes.end();
|
||||
int r = 0;
|
||||
for(; curn != endn; curn++) {
|
||||
n = *curn;
|
||||
n->node_id = r;
|
||||
r++;
|
||||
}
|
||||
|
||||
typedef std::pair < int, int > E; //our edge type
|
||||
|
||||
weightmap = get(edge_weight, vg);
|
||||
|
||||
//now add all our edges to the graph
|
||||
cur = big->edges.begin();
|
||||
end = big->edges.end();
|
||||
for(r = 0; cur != end; cur++, r++) {
|
||||
e = *cur;
|
||||
if(e->from == e->to)
|
||||
continue;
|
||||
|
||||
// printf("A %d/%d (%.3f,%.3f,%.3f) -> (%.3f,%.3f,%.3f) d=%.3f\n", r, big->edges.size(), e->from->x, e->from->y, e->from->z, e->to->x, e->to->y, e->to->z, e->from->Dist2(e->to));
|
||||
EdgeDesc ed;
|
||||
bool inserted;
|
||||
tie(ed, inserted) = add_edge(e->from->node_id, e->to->node_id, vg);
|
||||
if(set_weights)
|
||||
weightmap[ed] = int(e->from->Dist2(e->to));
|
||||
else
|
||||
weightmap[ed] = 1;
|
||||
em[e] = ed;
|
||||
// e->edge_id = ed;
|
||||
}
|
||||
//now we should have a nice happy undirected graph...
|
||||
}
|
||||
|
||||
void run_min_spanning_tree(MyGraph &vg, property_map<MyGraph, edge_weight_t>::type &weightmap, map<PathEdge *, EdgeDesc> &em, PathGraph *big, int start_node) {
|
||||
int noedge = 0;
|
||||
list<PathEdge *> out_paths;
|
||||
|
||||
vector < EdgeDesc > spanning_tree;
|
||||
|
||||
kruskal_minimum_spanning_tree(vg, back_inserter(spanning_tree));
|
||||
|
||||
vector < EdgeDesc >::iterator cur,end;
|
||||
list<PathEdge*>::iterator cure,ende;
|
||||
cur = spanning_tree.begin();
|
||||
end = spanning_tree.end();
|
||||
for(; cur != end; cur++) {
|
||||
//find the edge
|
||||
cure = big->edges.begin();
|
||||
ende = big->edges.end();
|
||||
for(; cure != ende; cure++) {
|
||||
if(em[*cure] == *cur) {
|
||||
out_paths.push_back(new PathEdge((*cure)->from, (*cure)->to));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
noedge = big->edges.size() - out_paths.size() - 1;
|
||||
printf("Ran Min Spanning Tree: %d paths were eliminated.\n", noedge);
|
||||
|
||||
|
||||
/*
|
||||
//make our results list.
|
||||
vector < VertDesc > p(num_vertices(vg));
|
||||
|
||||
//finally run the stupid algorithm.
|
||||
prim_minimum_spanning_tree(vg, &p[0], root_vertex(start_node));
|
||||
|
||||
|
||||
for (std::size_t i = 0; i != p.size(); ++i) {
|
||||
if (p[i] != i) {
|
||||
out_paths.push_back(new PathEdge(big->nodes[p[i]], big->nodes[i]));
|
||||
} else {
|
||||
//no edge here...
|
||||
noedge++;
|
||||
}
|
||||
}
|
||||
printf("Ran Min Spanning Tree: %d nodes were disconnected.\n", noedge);
|
||||
|
||||
|
||||
*/
|
||||
|
||||
//now swap out our edge list to the MST.
|
||||
big->edges = out_paths;
|
||||
}
|
||||
|
||||
void find_disjoint_grids(Map *map, MyGraph &vg, PathGraph *big, const char *fname, vector<int> &start_nodes, vector<PathGraph *> &disjoints) {
|
||||
|
||||
vector< vector<int> > D;
|
||||
vector<int> counts;
|
||||
vector<int> disjoint_counts;
|
||||
vector<int> first_node;
|
||||
|
||||
//color the graph and get us the info we need to do out job
|
||||
color_disjoint_graphs(big, vg, map, fname, D, counts, disjoint_counts, first_node);
|
||||
|
||||
|
||||
//find the biggest grid, that one gets to be included no matter what
|
||||
int best_graph = 0;
|
||||
int best_count = counts[0];
|
||||
unsigned int r;
|
||||
for(r = 1; r < counts.size(); r++) {
|
||||
if(best_count < counts[r]) {
|
||||
best_count = counts[r];
|
||||
best_graph = r;
|
||||
}
|
||||
}
|
||||
disjoint_counts[best_graph] = 1;
|
||||
|
||||
|
||||
//break up the graphs based on color if we want them.
|
||||
vector<int>::iterator ccur,djcur,fcur,cend;
|
||||
list<PathEdge*>::iterator cur,end;
|
||||
PathEdge *e;
|
||||
PathGraph *pg;
|
||||
ccur = counts.begin();
|
||||
djcur = disjoint_counts.begin();
|
||||
fcur = first_node.begin();
|
||||
cend = counts.end();
|
||||
int color = 0;
|
||||
for(; ccur != cend; ccur++, djcur++, fcur++, color++) {
|
||||
int count = *ccur;
|
||||
int dj = *djcur;
|
||||
// int fn = *fcur;
|
||||
|
||||
if(dj < 1)
|
||||
continue; //skip disjoint sets not marked for use.
|
||||
|
||||
if(count < MIN_DISJOINT_NODES)
|
||||
continue; //make sure we have a reasonable node count
|
||||
|
||||
pg = new PathGraph();
|
||||
|
||||
cur = big->edges.begin();
|
||||
end = big->edges.end();
|
||||
for(; cur != end; cur++) {
|
||||
e = *cur;
|
||||
if(e->from->color != e->to->color) {
|
||||
printf("Color Mismatch %d-%d: #%d(%.3f,%.3f,%.3f) -> #%d(%.3f,%.3f,%.3f)\n", e->from->color, e->to->color, e->from->node_id, e->from->x, e->from->y, e->from->z, e->to->node_id, e->to->x, e->to->y, e->to->z);
|
||||
//... what to do...
|
||||
}
|
||||
//just use the color of the from node
|
||||
if(e->from->color == color) {
|
||||
pg->edges.push_back(e);
|
||||
}
|
||||
}
|
||||
|
||||
//get our list of nodes based on our edge list.
|
||||
rebuild_node_list(pg->edges, pg->nodes, NULL);
|
||||
|
||||
disjoints.push_back(pg);
|
||||
start_nodes.push_back(1); //each graph only contains its own nodes, so any node will work.
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
int best_graph = 0;
|
||||
int best_count = counts[0];
|
||||
unsigned int r;
|
||||
for(r = 1; r < counts.size(); r++) {
|
||||
if(best_count < counts[r]) {
|
||||
best_count = counts[r];
|
||||
best_graph = r;
|
||||
}
|
||||
// printf("Graph %d has %d edges\n", r, counts[r]);
|
||||
}
|
||||
|
||||
start_node = first_node[best_graph];
|
||||
printf("Best sub-graph: chose #%d of %d, has %d nodes, and contains node %d\n", best_graph, counts.size()-1, best_count, start_node);
|
||||
//todo: eliminate other graphs...
|
||||
*/
|
||||
|
||||
/* list<PathEdge*>::iterator cure,ende;
|
||||
PathEdge *e;
|
||||
cure = big->edges.begin();
|
||||
ende = big->edges.end();
|
||||
for(; cure != ende; cure++) {
|
||||
e = *cure;
|
||||
if(e->from->color != e->to->color) {
|
||||
printf("Color Mismatch %d-%d: #%d(%.3f,%.3f,%.3f) -> #%d(%.3f,%.3f,%.3f)\n", e->from->color, e->to->color, e->from->node_id, e->from->x, e->from->y, e->from->z, e->to->node_id, e->to->x, e->to->y, e->to->z);
|
||||
} else if(e->from->color != best_graph) {
|
||||
printf("Color %d: (%.3f,%.3f,%.3f) -> (%.3f,%.3f,%.3f)\n", e->from->color, e->from->x, e->from->y, e->from->z, e->to->x, e->to->y, e->to->z);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const int INT_LIMIT = (std::numeric_limits < int >::max)();
|
||||
|
||||
void color_disjoint_graphs(
|
||||
PathGraph *big,
|
||||
MyGraph &vg,
|
||||
Map *map,
|
||||
const char *fname,
|
||||
|
||||
vector< vector<int> > &D, //output
|
||||
vector<int> &counts, //output
|
||||
vector<int> &disjoint_counts, //output
|
||||
vector<int> &first_node //output
|
||||
) {
|
||||
|
||||
|
||||
int count = big->nodes.size();
|
||||
// int r;
|
||||
|
||||
// vector< vector<int> > D(count, vector<int>(count, INT_LIMIT));
|
||||
// vector<int> counts(1, 0);
|
||||
// vector<int> first_node(1, 0);
|
||||
|
||||
//make sure everything is inited right...
|
||||
D.resize(0);
|
||||
D.resize(count, vector<int>(count, INT_LIMIT));
|
||||
counts.resize(1);
|
||||
counts[0] = 0;
|
||||
disjoint_counts.resize(1);
|
||||
disjoint_counts[0] = 0;
|
||||
first_node.resize(1);
|
||||
first_node[0] = 0;
|
||||
|
||||
//make up a weight map with all 1s, done while building now
|
||||
/* property_map < MyGraph, edge_weight_t >::type w = get(edge_weight, vg);
|
||||
graph_traits < MyGraph >::edge_iterator e, e_end;
|
||||
for (boost::tie(e, e_end) = edges(vg); e != e_end; ++e)
|
||||
w[*e] = 1;*/
|
||||
|
||||
|
||||
johnson_all_pairs_shortest_paths(vg, D);
|
||||
|
||||
list<PathNode*>::iterator cur,end,cur2;
|
||||
PathNode *n,*f;
|
||||
|
||||
int cur_color = 1;
|
||||
int cc,djc;
|
||||
|
||||
//clear node colors
|
||||
cur = big->nodes.begin();
|
||||
end = big->nodes.end();
|
||||
for(; cur != end; cur++) {
|
||||
n = *cur;
|
||||
n->color = 0;
|
||||
}
|
||||
|
||||
|
||||
//color the graph based on reachability, basically labeling subgraphs
|
||||
//this finds the number of nodes reachable from each node
|
||||
//which is used to pick the best disconnected graph in the tree.
|
||||
//its a series of wrong bullshit that forces us to do this, but it works
|
||||
cur = big->nodes.begin();
|
||||
end = big->nodes.end();
|
||||
for(; cur != end; cur++) {
|
||||
n = *cur;
|
||||
if(n->color != 0)
|
||||
continue; //allready visited
|
||||
|
||||
//printf("New Color at: (%.3f,%.3f,%.3f)\n", n->x, n->y, n->z);
|
||||
|
||||
cc = 1;
|
||||
djc = 0;
|
||||
|
||||
if(n->disjoint)
|
||||
djc++;
|
||||
|
||||
n->color = cur_color;
|
||||
cur_color++;
|
||||
|
||||
cur2 = cur;
|
||||
for(; cur2 != end; cur2++) {
|
||||
f = *cur2;
|
||||
if(f->color == 0 && D[n->node_id][f->node_id] != INT_LIMIT) {
|
||||
cc++;
|
||||
f->color = n->color;
|
||||
}
|
||||
if(f->disjoint)
|
||||
djc++;
|
||||
}
|
||||
counts.push_back(cc);
|
||||
disjoint_counts.push_back(djc);
|
||||
first_node.push_back(n->node_id);
|
||||
}
|
||||
|
||||
#ifdef DRAW_ALL_COLORS
|
||||
|
||||
if(fname != NULL) {
|
||||
printf("Drawing with %d seperate sub-graphs from %d nodes and %d edges\n", cur_color-1, big->nodes.size(), big->edges.size());
|
||||
|
||||
FILE *pngout;
|
||||
pngout = fopen(fname, "wb");
|
||||
if(pngout == NULL) {
|
||||
printf("Unable to open %s\n", fname);
|
||||
return;
|
||||
}
|
||||
|
||||
gdImagePtr im;
|
||||
int minx = int(map->GetMinX());
|
||||
int maxx = int(map->GetMaxX());
|
||||
int miny = int(map->GetMinY());
|
||||
int maxy = int(map->GetMaxY());
|
||||
|
||||
im = gdImageCreate((maxx - minx)/IMAGE_SCALE, (maxy - miny)/IMAGE_SCALE);
|
||||
// im = gdImageCreate(maxx - minx, maxy - miny);
|
||||
|
||||
//allocate this first, to make it the BG color.
|
||||
/*int black =*/ gdImageColorAllocate(im, 0, 0, 0);
|
||||
|
||||
// int grey = gdImageColorAllocate(im, 100, 100, 100);
|
||||
|
||||
int *clist = new int[cur_color];
|
||||
int r;
|
||||
for(r = 0; r < cur_color; r++) {
|
||||
clist[r] = gdImageColorAllocate(im, rand()%255, rand()%255, rand()%255);
|
||||
}
|
||||
|
||||
{
|
||||
list<PathEdge*>::iterator cur,end;
|
||||
PathEdge *e;
|
||||
|
||||
cur = big->edges.begin();
|
||||
end = big->edges.end();
|
||||
for(; cur != end; cur++) {
|
||||
e = *cur;
|
||||
int x1 = int(e->from->x) - minx;
|
||||
int y1 = int(e->from->y) - miny;
|
||||
int x2 = int(e->to->x) - minx;
|
||||
int y2 = int(e->to->y) - miny;
|
||||
x1 /= IMAGE_SCALE;
|
||||
y1 /= IMAGE_SCALE;
|
||||
x2 /= IMAGE_SCALE;
|
||||
y2 /= IMAGE_SCALE;
|
||||
gdImageLine(im, x1, y1, x2, y2, clist[e->from->color]);
|
||||
}
|
||||
}
|
||||
delete[] clist;
|
||||
|
||||
gdImagePng(im, pngout);
|
||||
gdImageDestroy(im);
|
||||
|
||||
fclose(pngout);
|
||||
|
||||
printf("Wrote image: %s\n", fname);
|
||||
}
|
||||
#endif //DRAW_ALL_COLORS
|
||||
}
|
||||
|
||||
void calc_path_lengths(Map *map, MyGraph &vg, PathGraph *big, map<PathEdge *, EdgeDesc> &em, const char *fname) {
|
||||
|
||||
vector<int> counts;
|
||||
vector<int> disjoint_counts;
|
||||
vector<int> first_node;
|
||||
vector< vector<int> > D;
|
||||
list<PathNode*>::iterator cur,end;
|
||||
PathNode *n;
|
||||
|
||||
/*
|
||||
Node distances:
|
||||
1. find the root node.
|
||||
- find the longest path from each node to any other node
|
||||
- the node with the shortest of these longest paths is the root
|
||||
2. record each node's distance from that root node
|
||||
*/
|
||||
|
||||
//color the graph and get us the info we need to do out job
|
||||
color_disjoint_graphs(big, vg, map, fname, D, counts, disjoint_counts, first_node);
|
||||
|
||||
|
||||
vector<int>::iterator curp,endp;
|
||||
|
||||
int shortest_node = 0;
|
||||
int shortest = 0xFFFFFF;
|
||||
int the_longest = 0;
|
||||
int longest_node = 0;
|
||||
|
||||
//stores the longest path from each node
|
||||
vector<int> longest_dists(D.size(), 0);
|
||||
|
||||
//find the node with the longest path of all, so we know what
|
||||
//tree we are trying to find the root of
|
||||
cur = big->nodes.begin();
|
||||
end = big->nodes.end();
|
||||
for(; cur != end; cur++) {
|
||||
n = *cur;
|
||||
vector<int> &cv = D[n->node_id];
|
||||
curp = cv.begin();
|
||||
endp = cv.end();
|
||||
int longest = 0;
|
||||
int discount = 0;
|
||||
for(; curp != endp; curp++) {
|
||||
if(*curp == INT_LIMIT) {
|
||||
discount++;
|
||||
continue;
|
||||
}
|
||||
if(*curp > longest)
|
||||
longest = *curp;
|
||||
}
|
||||
|
||||
longest_dists[n->node_id] = longest;
|
||||
|
||||
if(longest > the_longest) {
|
||||
the_longest = longest;
|
||||
longest_node = n->node_id;
|
||||
}
|
||||
}
|
||||
|
||||
//find the node with the shortest, longest path
|
||||
//the idea is to locate the 'root' of the tree.
|
||||
cur = big->nodes.begin();
|
||||
end = big->nodes.end();
|
||||
for(; cur != end; cur++) {
|
||||
n = *cur;
|
||||
vector<int> &cv = D[n->node_id];
|
||||
if(cv[longest_node] == INT_LIMIT)
|
||||
continue; //this node cannot reach the root
|
||||
|
||||
int longest = longest_dists[n->node_id];
|
||||
//n->longest_path = longest;
|
||||
//printf("Node %d's longest path is %d\n", n->node_id, longest);
|
||||
|
||||
if(longest < shortest) {
|
||||
shortest = longest;
|
||||
shortest_node = n->node_id;
|
||||
}
|
||||
}
|
||||
|
||||
//now we have our root, set each node to their distance from the root
|
||||
vector<int> &root_dists = D[shortest_node];
|
||||
printf("The tree's root is %d\n", shortest_node);
|
||||
cur = big->nodes.begin();
|
||||
end = big->nodes.end();
|
||||
for(; cur != end; cur++) {
|
||||
n = *cur;
|
||||
if(n->node_id == shortest_node)
|
||||
n->longest_path = 0;
|
||||
else
|
||||
n->longest_path = root_dists[n->node_id];
|
||||
//printf("Node %d's distance from root is %d\n", n->node_id, root_dists[n->node_id]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
the reachability is the number of nodes which we could possibly get to
|
||||
by traveling each direction across an edge.
|
||||
|
||||
to find reachability:
|
||||
loop through each edge e
|
||||
Remove that edge from the graph
|
||||
color_disjoint_graphs
|
||||
fc = count number of nodes with the same color as 'e->from'
|
||||
tc = count number of nodes with the same color as 'e->to'
|
||||
e->normal_reach = tc;
|
||||
e->reverse_reach = fc;
|
||||
*/
|
||||
property_map<MyGraph, edge_weight_t>::type weightmap;
|
||||
weightmap = get(edge_weight, vg);
|
||||
|
||||
int tc,fc;
|
||||
int from_color, to_color;
|
||||
|
||||
printf("Finding lengths (%d dots)", 1+big->edges.size()/10);
|
||||
int pos = 0;
|
||||
|
||||
PathEdge *e;
|
||||
list<PathEdge*>::iterator cur4,end4;
|
||||
cur4 = big->edges.begin();
|
||||
end4 = big->edges.end();
|
||||
for(; cur4 != end4; cur4++, pos++) {
|
||||
if(pos % 10 == 0) {
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
}
|
||||
e = *cur4;
|
||||
|
||||
//remove this edge from the boost graph..
|
||||
remove_edge(em[e], vg);
|
||||
|
||||
//color the graph and get us the info we need to do our job
|
||||
//char out[64];
|
||||
//sprintf(out, "lengraph-%d.png", pos);
|
||||
color_disjoint_graphs(big, vg, map,
|
||||
NULL,
|
||||
D, counts, disjoint_counts, first_node);
|
||||
|
||||
//add the edge back in
|
||||
EdgeDesc ed;
|
||||
bool inserted;
|
||||
tie(ed, inserted) = add_edge(e->from->node_id, e->to->node_id, vg);
|
||||
em[e] = ed;
|
||||
weightmap[em[e]] = int(e->from->Dist2(e->to)); //cause its a new edge
|
||||
|
||||
//make sure this stupid thing worked.
|
||||
if(e->from->color == e->to->color) {
|
||||
printf("Cycle detected in MST... WTF?\n");
|
||||
e->normal_reach = -1;
|
||||
e->reverse_reach = -1;
|
||||
continue;
|
||||
}
|
||||
from_color = e->from->color;
|
||||
to_color = e->to->color;
|
||||
|
||||
//count our crap.
|
||||
tc = 0;
|
||||
fc = 0;
|
||||
cur = big->nodes.begin();
|
||||
end = big->nodes.end();
|
||||
for(; cur != end; cur++) {
|
||||
n = *cur;
|
||||
if(n->color == to_color)
|
||||
tc++;
|
||||
else if(n->color == from_color)
|
||||
fc++;
|
||||
}
|
||||
|
||||
//put it on our edge
|
||||
e->normal_reach = tc;
|
||||
e->reverse_reach = fc;
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
//re-color the graph, since we dicked with it above
|
||||
color_disjoint_graphs(big, vg, map, fname, D, counts, disjoint_counts, first_node);
|
||||
choose_biggest_graph(big, counts, first_node);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void just_color_the_damned_thing(Map *map, PathGraph *big, const char *fname) {
|
||||
MyGraph vg(big->nodes.size());
|
||||
property_map<MyGraph, edge_weight_t>::type weightlist_mst;
|
||||
std::map<PathEdge *, EdgeDesc> edgemap_mst;
|
||||
build_boost_graph(vg, weightlist_mst, edgemap_mst, big, true);
|
||||
|
||||
vector<int> counts;
|
||||
vector<int> disjoint_counts;
|
||||
vector<int> first_node;
|
||||
vector< vector<int> > D;
|
||||
list<PathNode*>::iterator cur,end;
|
||||
|
||||
color_disjoint_graphs(big, vg, map, fname, D, counts, disjoint_counts, first_node);
|
||||
}
|
||||
|
||||
/*
|
||||
void calc_path_lengths(Map *map, MyGraph &vg, PathGraph *big, vector< vector<int> > &D, const char *fname) {
|
||||
|
||||
// vector< vector<int> > D;
|
||||
vector<int> counts;
|
||||
vector<int> first_node;
|
||||
|
||||
|
||||
choose_biggest_graph(big, counts, first_node);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
//assumes that the graph is freshly colored disjoint, and counts/first_node are results from it
|
||||
void choose_biggest_graph(PathGraph *big, vector<int> &counts, vector<int> &first_node) {
|
||||
int best_graph = 0;
|
||||
int best_count = counts[0];
|
||||
unsigned int r;
|
||||
for(r = 1; r < counts.size(); r++) {
|
||||
if(best_count < counts[r]) {
|
||||
best_count = counts[r];
|
||||
best_graph = r;
|
||||
}
|
||||
printf("Graph %d has %d edges\n", r, counts[r]);
|
||||
}
|
||||
|
||||
printf("Best Tree: Detected %d graphs in the MST, selected #%d\n", counts.size(), best_graph);
|
||||
|
||||
list<PathNode*> new_nodes;
|
||||
list<PathEdge*> new_edges;
|
||||
|
||||
//rebuild the node list to include only nodes which are in
|
||||
//the best graph, also only keeping the edges which connect them.
|
||||
std::map<PathNode *, int> havenodelist;
|
||||
list<PathEdge*>::iterator cur4,end4;
|
||||
cur4 = big->edges.begin();
|
||||
end4 = big->edges.end();
|
||||
for(; cur4 != end4; cur4++) {
|
||||
PathEdge *e = *cur4;
|
||||
|
||||
//remove the edge if it is not the main color
|
||||
//this also causes the removal of the nodes if they
|
||||
//are not used in any other edges.
|
||||
if(e->from->color != e->to->color) {
|
||||
printf("Miscolor.\n");
|
||||
continue;
|
||||
}
|
||||
if(e->from->color != best_graph) {
|
||||
continue;
|
||||
}
|
||||
if(e->to->color != best_graph) {
|
||||
continue;
|
||||
}
|
||||
|
||||
new_edges.push_back(e);
|
||||
|
||||
if(havenodelist.count(e->from) != 1) {
|
||||
e->from->final_id = new_nodes.size();
|
||||
new_nodes.push_back(e->from);
|
||||
havenodelist[e->from] = 1;
|
||||
}
|
||||
if(havenodelist.count(e->to) != 1) {
|
||||
e->to->final_id = new_nodes.size();
|
||||
new_nodes.push_back(e->to);
|
||||
havenodelist[e->to] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Best Tree: Removed %d nodes and %d edges which were disconnected.\n",
|
||||
big->nodes.size() - new_nodes.size(), big->edges.size() - new_edges.size());
|
||||
big->nodes = new_nodes;
|
||||
big->edges = new_edges;
|
||||
}
|
||||
|
||||
void consolidate_cross_graphs(Map *map, PathGraph *big, PathGraph *excess, MyGraph &cross_graph, const char *fname) {
|
||||
|
||||
vector< vector<int> > D;
|
||||
vector<int> counts;
|
||||
vector<int> disjoint_counts;
|
||||
vector<int> first_node;
|
||||
|
||||
//color the graph and get us the info we need to do our job
|
||||
color_disjoint_graphs(big, cross_graph, map, fname, D, counts, disjoint_counts, first_node);
|
||||
}
|
||||
|
||||
void find_path_info(Map *map, MyGraph &vg, vector< vector<PathEdge*> > &path_finding, PathGraph *big) {
|
||||
//make sure our path finding vector is big enough.
|
||||
{
|
||||
int size = big->nodes.size();
|
||||
vector<PathEdge*> tmp(size, (PathEdge*)NULL);
|
||||
path_finding.resize(0);
|
||||
path_finding.resize(size, tmp);
|
||||
}
|
||||
|
||||
vector< vector<int> > D;
|
||||
vector<int> counts;
|
||||
vector<int> disjoint_counts;
|
||||
vector<int> first_node;
|
||||
|
||||
//color the graph and get us the info we need to do our job
|
||||
color_disjoint_graphs(big, vg, map, NULL, D, counts, disjoint_counts, first_node);
|
||||
|
||||
//figure out what edges link to each node.
|
||||
std::map<PathNode*, vector<PathEdge*> > node_edges;
|
||||
find_node_edges(big, node_edges);
|
||||
|
||||
|
||||
//for each node, find best edge to reach each other node.
|
||||
list<PathNode*>::iterator cur,end;
|
||||
PathNode *n;
|
||||
vector<int>::iterator curp,endp;
|
||||
vector<PathEdge *>::iterator cure,ende;
|
||||
int r;
|
||||
cur = big->nodes.begin();
|
||||
end = big->nodes.end();
|
||||
for(; cur != end; cur++) {
|
||||
n = *cur;
|
||||
//get all our vector refs that we need since this is kinda expensive
|
||||
vector<int> &cv = D[n->node_id]; //distance array
|
||||
vector<PathEdge *> &pf = path_finding[n->node_id]; //result
|
||||
vector<PathEdge *> &el = node_edges[n]; //our edges
|
||||
curp = cv.begin();
|
||||
endp = cv.end();
|
||||
for(r = 0; curp != endp; curp++, r++) {
|
||||
if(n->node_id == r) {
|
||||
//this is the end of the path, we cannot take an edge to reach ourself
|
||||
pf[r] = NULL;
|
||||
continue;
|
||||
}
|
||||
int my_dist = *curp;
|
||||
if(my_dist == INT_MAX) {
|
||||
//this node is unreachable.
|
||||
pf[r] = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
//else, node r reachable from us, find best edge.
|
||||
cure = el.begin();
|
||||
ende = el.end();
|
||||
// int shortest;
|
||||
PathEdge *ce;
|
||||
PathNode *cn;
|
||||
//for each edge
|
||||
for(; cure != ende; cure++) {
|
||||
ce = *cure;
|
||||
//find the node other than ourself on the edge
|
||||
if(ce->from == n)
|
||||
cn = ce->to;
|
||||
else
|
||||
cn = ce->from;
|
||||
//see how far away this node is
|
||||
int cdist = D[cn->node_id][r];
|
||||
if(cdist < my_dist) {
|
||||
//found one which is closer... due to min span tree, there
|
||||
//should only be one path possible so just go with it.
|
||||
pf[r] = ce;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//assume that this node got assigned.. next
|
||||
if(pf[r] == NULL) {
|
||||
printf("Node id %d was not able to find a good path to node %d\n", n->node_id, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef BOOSTCRAP_H
|
||||
#define BOOSTCRAP_H
|
||||
|
||||
#include "apathing.h"
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/graph/adjacency_list.hpp>
|
||||
#include <boost/graph/prim_minimum_spanning_tree.hpp>
|
||||
#include <boost/graph/kruskal_min_spanning_tree.hpp>
|
||||
#include <boost/graph/johnson_all_pairs_shortest.hpp>
|
||||
using namespace boost;
|
||||
|
||||
|
||||
//man I hate boost
|
||||
typedef adjacency_list < vecS, vecS, undirectedS,
|
||||
property<vertex_distance_t, int>, property < edge_weight_t, int > >
|
||||
MyGraph;
|
||||
|
||||
typedef graph_traits < MyGraph >::vertex_descriptor VertDesc;
|
||||
typedef graph_traits < MyGraph >::edge_descriptor EdgeDesc;
|
||||
|
||||
void build_boost_graph(MyGraph &vg, property_map<MyGraph, edge_weight_t>::type &weightmap, map<PathEdge *, EdgeDesc> &em, PathGraph *big, bool set_weights = true);
|
||||
void run_min_spanning_tree(MyGraph &vg, property_map<MyGraph, edge_weight_t>::type &weightmap, map<PathEdge *, EdgeDesc> &em, PathGraph *big, int start_node);
|
||||
void find_disjoint_grids(Map *map, MyGraph &vg, PathGraph *big, const char *file, vector<int> &start_nodes, vector<PathGraph *> &disjoints);
|
||||
void calc_path_lengths(Map *map, MyGraph &vg, PathGraph *big, map<PathEdge *, EdgeDesc> &em, const char *fname);
|
||||
void color_disjoint_graphs(PathGraph *big, MyGraph &vg, Map *map, const char *fname, vector< vector<int> > &D, vector<int> &counts, vector<int> &disjoint_counts, vector<int> &first_node );
|
||||
void count_crossing_lines(list<PathEdge *> &edges, PathGraph *out, PathGraph *excess, map<PathEdge*, vector<GPoint> > &cross_list);
|
||||
void consolidate_cross_graphs(Map *map, PathGraph *cross_big, PathGraph *cross_excess, MyGraph &cross_graph, const char *fname);
|
||||
void just_color_the_damned_thing(Map *map, PathGraph *big, const char *fname);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include "gpoint.h"
|
||||
|
||||
|
||||
GPoint::GPoint() {
|
||||
x = 0;
|
||||
y = 0;
|
||||
z = 0;
|
||||
}
|
||||
|
||||
GPoint::GPoint(VERTEX &v) {
|
||||
x = v.x;
|
||||
y = v.y;
|
||||
z = v.z;
|
||||
}
|
||||
|
||||
GPoint::GPoint(float ix, float iy, float iz) {
|
||||
x = ix;
|
||||
y = iy;
|
||||
z = iz;
|
||||
}
|
||||
|
||||
GPoint::GPoint(const GPoint &them) {
|
||||
x = them.x;
|
||||
y = them.y;
|
||||
z = them.z;
|
||||
}
|
||||
|
||||
//dot of x,y,z
|
||||
float GPoint::dot3(const GPoint &them) const {
|
||||
return((x * them.x) + (y * them.y) +
|
||||
(z * them.z));
|
||||
}
|
||||
|
||||
//cross product
|
||||
GPoint GPoint::cross(const GPoint &them) const {
|
||||
return(GPoint(y * them.z - z * them.y,
|
||||
z * them.x - x * them.z,
|
||||
x * them.y - y * them.x));
|
||||
}
|
||||
|
||||
|
||||
const GPoint &GPoint::operator+=(const GPoint &them) {
|
||||
x += them.x;
|
||||
y += them.y;
|
||||
z += them.z;
|
||||
return(*this);
|
||||
}
|
||||
const GPoint &GPoint::operator*=(const float num) {
|
||||
x *= num;
|
||||
y *= num;
|
||||
z *= num;
|
||||
return(*this);
|
||||
}
|
||||
|
||||
GPoint operator-(const GPoint &v1, const GPoint &v2) {
|
||||
return(GPoint(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z));
|
||||
}
|
||||
|
||||
//ordering on X only
|
||||
bool operator<(const GPoint &v1, const GPoint &v2) {
|
||||
return(v1.x < v2.x);
|
||||
}
|
||||
|
||||
//ordering on X only
|
||||
bool operator>(const GPoint &v1, const GPoint &v2) {
|
||||
return(v1.x > v2.x);
|
||||
}
|
||||
|
||||
|
||||
GVector::GVector() : GPoint() {
|
||||
W = 0;
|
||||
}
|
||||
|
||||
GVector::GVector(const GPoint &them) : GPoint(them) {
|
||||
W = 1.0f;
|
||||
}
|
||||
|
||||
GVector::GVector(const GPoint &from, const GPoint &to)
|
||||
: GPoint(to.x - from.x, to.y - from.y, to.z - from.z)
|
||||
{
|
||||
W = 1.0f;
|
||||
}
|
||||
|
||||
GVector::GVector(float x, float y, float z, float w) : GPoint(x, y, z) {
|
||||
W = w;
|
||||
}
|
||||
|
||||
//dot product of x,y,z,w
|
||||
float GVector::dot4(const GVector &them) const {
|
||||
return((x * them.x) + (y * them.y) +
|
||||
(z * them.z) + (W * them.W));
|
||||
}
|
||||
|
||||
//dot product of x,y,z+w
|
||||
float GVector::dot4(const GPoint &them) const {
|
||||
return((x * them.x) + (y * them.y) +
|
||||
(z * them.z) + W);
|
||||
}
|
||||
|
||||
float GVector::length() {
|
||||
return(sqrt((x * x) + (y * y) + (z * z)));
|
||||
}
|
||||
|
||||
void GVector::normalize() {
|
||||
float len = length(); //stupid square roots take forever
|
||||
x /= len;
|
||||
y /= len;
|
||||
z /= len;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifndef GPOINT_H
|
||||
#define GPOINT_H
|
||||
|
||||
#include "../zone/map.h"
|
||||
|
||||
class GPoint {
|
||||
public:
|
||||
GPoint();
|
||||
GPoint(const GPoint &them);
|
||||
GPoint(VERTEX &v);
|
||||
GPoint(float x, float y, float z);
|
||||
|
||||
inline void operator()(float nx, float ny, float nz) { x = nx; y = ny; z = nz; }
|
||||
|
||||
GPoint cross(const GPoint &them) const;
|
||||
float dot3(const GPoint &them) const;
|
||||
|
||||
const GPoint &operator+=(const GPoint &them);
|
||||
const GPoint &operator*=(const float num);
|
||||
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
|
||||
};
|
||||
GPoint operator-(const GPoint &v1, const GPoint &v2);
|
||||
bool operator<(const GPoint &v1, const GPoint &v2);
|
||||
bool operator>(const GPoint &v1, const GPoint &v2);
|
||||
|
||||
class GVector : public GPoint {
|
||||
public:
|
||||
GVector();
|
||||
GVector(const GPoint &them);
|
||||
GVector(const GPoint &from, const GPoint &to);
|
||||
GVector(float x, float y, float z, float w = 1.0f);
|
||||
|
||||
inline void operator()(float nx, float ny, float nz, float nw) { x = nx; y = ny; z = nz; W = nw; }
|
||||
float dot4(const GVector &them) const;
|
||||
float dot4(const GPoint &them) const;
|
||||
void normalize();
|
||||
float length();
|
||||
|
||||
|
||||
float W;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
|
||||
Fear Pathing generation utility.
|
||||
(c) 2005 Father Nitwit
|
||||
|
||||
*/
|
||||
#include "../common/types.h"
|
||||
#include "../zone/map.h"
|
||||
#include "../common/rdtsc.h"
|
||||
#include "quadtree.h"
|
||||
#include "apathing.h"
|
||||
#include <stdio.h>
|
||||
#include <mysql.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <gd.h>
|
||||
|
||||
|
||||
|
||||
bool load_paths_from_db(MYSQL *m, Map *map, const char *zone, list<PathGraph*> &db_paths, list<PathNode*> &end_points) {
|
||||
char query[512];
|
||||
|
||||
sprintf(query,
|
||||
"SELECT x,y,z,gridid FROM grid_entries,zone "
|
||||
"WHERE zone.zoneidnumber=zoneid AND short_name='%s' "
|
||||
"ORDER BY gridid,number", zone);
|
||||
if(mysql_query(m, query) != 0) {
|
||||
printf("Unable to query: %s\n", mysql_error(m));
|
||||
return(false);
|
||||
}
|
||||
|
||||
MYSQL_RES *res = mysql_store_result(m);
|
||||
if(res == NULL) {
|
||||
printf("Unable to store res: %s\n", mysql_error(m));
|
||||
return(false);
|
||||
}
|
||||
|
||||
MYSQL_ROW row;
|
||||
|
||||
PathNode *cur = NULL, *last = NULL, *first = NULL;
|
||||
PathGraph *g = NULL;
|
||||
int cur_g = -1,last_g = -1;
|
||||
|
||||
// int lid = 0;
|
||||
|
||||
while((row = mysql_fetch_row(res))) {
|
||||
last = cur;
|
||||
cur = new PathNode;
|
||||
// cur->load_id = lid++;
|
||||
cur->x = atof(row[0]);
|
||||
cur->y = atof(row[1]);
|
||||
cur->z = atof(row[2]);
|
||||
cur_g = atoi(row[3]);
|
||||
if(cur_g != last_g) {
|
||||
if(g != NULL) {
|
||||
//if we have a first and last node for this path
|
||||
//and they are not the same node, try to connect them.
|
||||
if(first != NULL && last != NULL && first != last && last->Dist2(first) < ENDPOINT_CONNECT_MAX_DISTANCE*ENDPOINT_CONNECT_MAX_DISTANCE) {
|
||||
if(CheckLOS(map, last, first))
|
||||
g->add_edge(last, first);
|
||||
}
|
||||
#ifdef LINK_PATH_ENDPOINTS
|
||||
if(first != last && last != NULL)
|
||||
end_points.push_back(last);
|
||||
#endif
|
||||
db_paths.push_back(g);
|
||||
}
|
||||
g = new PathGraph();
|
||||
first = cur;
|
||||
last_g = cur_g;
|
||||
last = NULL;
|
||||
}
|
||||
|
||||
g->nodes.push_back(cur);
|
||||
|
||||
#ifdef LINK_PATH_ENDPOINTS
|
||||
//this is a begining point
|
||||
if(last == NULL)
|
||||
end_points.push_back(cur);
|
||||
#endif
|
||||
|
||||
if(last != NULL) {
|
||||
#ifdef SPLIT_INVALID_PATHS
|
||||
if(CheckLOS(map, last, cur)) {
|
||||
g->edges.push_back(new PathEdge(last, cur));
|
||||
} else {
|
||||
//no LOS, split the path into two
|
||||
load_split_paths++;
|
||||
last_g = -1; //tell this thing to start over
|
||||
}
|
||||
#else
|
||||
g->edges.push_back(new PathEdge(last, cur));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
//handle the last active path
|
||||
if(g != NULL) {
|
||||
if(first != NULL && cur->Dist2(first) < ENDPOINT_CONNECT_MAX_DISTANCE*ENDPOINT_CONNECT_MAX_DISTANCE) {
|
||||
if(CheckLOS(map, cur, first))
|
||||
g->add_edge(cur, first);
|
||||
}
|
||||
db_paths.push_back(g);
|
||||
}
|
||||
|
||||
mysql_free_result(res);
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
bool load_spawns_from_db(MYSQL *m, const char *zone, list<PathNode*> &db_spawns) {
|
||||
char query[512];
|
||||
|
||||
sprintf(query,
|
||||
"SELECT x,y,z FROM spawn2 "
|
||||
"WHERE zone='%s'", zone);
|
||||
if(mysql_query(m, query) != 0) {
|
||||
printf("Unable to query: %s\n", mysql_error(m));
|
||||
return(false);
|
||||
}
|
||||
|
||||
MYSQL_RES *res = mysql_store_result(m);
|
||||
if(res == NULL) {
|
||||
printf("Unable to store res: %s\n", mysql_error(m));
|
||||
return(false);
|
||||
}
|
||||
|
||||
MYSQL_ROW row;
|
||||
|
||||
PathNode *cur = NULL;
|
||||
|
||||
while((row = mysql_fetch_row(res))) {
|
||||
cur = new PathNode;
|
||||
cur->x = atof(row[0]);
|
||||
cur->y = atof(row[1]);
|
||||
cur->z = atof(row[2]);
|
||||
db_spawns.push_back(cur);
|
||||
}
|
||||
|
||||
mysql_free_result(res);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
bool load_doors_from_db(MYSQL *m, const char *zone, list<PathNode*> &db_spawns) {
|
||||
char query[512];
|
||||
|
||||
sprintf(query,
|
||||
"SELECT pos_x,pos_y,pos_z FROM doors "
|
||||
"WHERE zone='%s'", zone);
|
||||
if(mysql_query(m, query) != 0) {
|
||||
printf("Unable to query: %s\n", mysql_error(m));
|
||||
return(false);
|
||||
}
|
||||
|
||||
MYSQL_RES *res = mysql_store_result(m);
|
||||
if(res == NULL) {
|
||||
printf("Unable to store res: %s\n", mysql_error(m));
|
||||
return(false);
|
||||
}
|
||||
|
||||
MYSQL_ROW row;
|
||||
|
||||
PathNode *cur = NULL;
|
||||
|
||||
while((row = mysql_fetch_row(res))) {
|
||||
cur = new PathNode;
|
||||
cur->x = atof(row[0]);
|
||||
cur->y = atof(row[1]);
|
||||
cur->z = atof(row[2]);
|
||||
//TODO: it would be nice if we could get to the middle of these
|
||||
//doors, not the edge of them which I assume these points are
|
||||
db_spawns.push_back(cur);
|
||||
}
|
||||
|
||||
mysql_free_result(res);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
bool load_hints_from_db(MYSQL *m, const char *zone, list<PathNode*> &db_spawns) {
|
||||
char query[512];
|
||||
|
||||
sprintf(query,
|
||||
"SELECT x,y,z,forced,disjoint FROM fear_hints "
|
||||
"WHERE zone='%s'", zone);
|
||||
if(mysql_query(m, query) != 0) {
|
||||
printf("Unable to query: %s\n", mysql_error(m));
|
||||
return(false);
|
||||
}
|
||||
|
||||
MYSQL_RES *res = mysql_store_result(m);
|
||||
if(res == NULL) {
|
||||
printf("Unable to store res: %s\n", mysql_error(m));
|
||||
return(false);
|
||||
}
|
||||
|
||||
MYSQL_ROW row;
|
||||
|
||||
PathNode *cur = NULL;
|
||||
|
||||
while((row = mysql_fetch_row(res))) {
|
||||
cur = new PathNode;
|
||||
cur->x = atof(row[0]);
|
||||
cur->y = atof(row[1]);
|
||||
cur->z = atof(row[2]);
|
||||
cur->forced = atoi(row[3])?true:false;
|
||||
cur->disjoint = atoi(row[4])?true:false;
|
||||
db_spawns.push_back(cur);
|
||||
}
|
||||
|
||||
mysql_free_result(res);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
bool load_settings_from_db(MYSQL *m, const char *zone) {
|
||||
char query[512];
|
||||
|
||||
sprintf(query,
|
||||
"SELECT use_doors, min_fix_z, max_fear_distance, image_scale, split_invalid_paths,"
|
||||
" link_path_endpoints, end_distance, split_long_min, split_long_step, same_dist, node_combine_dist,"
|
||||
" grid_combine_dist, close_all_los, cross_count, cross_min_length, cross_max_z_diff, cross_combine_dist,"
|
||||
" second_link_dist, link_max_dist, link_count"
|
||||
" FROM fear_settings"
|
||||
" WHERE zone='%s'", zone);
|
||||
if(mysql_query(m, query) != 0) {
|
||||
// printf("Unable to query: %s\n", mysql_error(m));
|
||||
return(false);
|
||||
}
|
||||
|
||||
MYSQL_RES *res = mysql_store_result(m);
|
||||
if(res == NULL) {
|
||||
// printf("Unable to store res: %s\n", mysql_error(m));
|
||||
return(false);
|
||||
}
|
||||
|
||||
MYSQL_ROW row;
|
||||
|
||||
int r = 0;
|
||||
if((row = mysql_fetch_row(res))) {
|
||||
INCLUDE_DOORS = atoi(row[r++])?true:false;
|
||||
MIN_FIX_Z = atof(row[r++]);
|
||||
FEAR_MAXIMUM_DISTANCE = atof(row[r++]);
|
||||
IMAGE_SCALE = atoi(row[r++]);
|
||||
SPLIT_INVALID_PATHS = atoi(row[r++])?true:false;
|
||||
LINK_PATH_ENDPOINTS = atoi(row[r++])?true:false;
|
||||
ENDPOINT_CONNECT_MAX_DISTANCE = atof(row[r++]);
|
||||
SPLIT_LINE_LENGTH = atof(row[r++]);
|
||||
SPLIT_LINE_INTERVAL = atof(row[r++]);
|
||||
CLOSE_ENOUGH = atof(row[r++]);
|
||||
CLOSE_ENOUGH_COMBINE = atof(row[r++]);
|
||||
MERGE_MIN_SECOND_DIST = atof(row[r++]);
|
||||
COMBINE_CHECK_ALL_LOS = atoi(row[r++])?true:false;
|
||||
CROSS_REDUCE_COUNT = atoi(row[r++]);
|
||||
CROSS_MIN_LENGTH = atof(row[r++]);
|
||||
CROSS_MAX_Z_DIFF = atof(row[r++]);
|
||||
CLOSE_ENOUGH_CROSS = atof(row[r++]);
|
||||
|
||||
SPAWN_MIN_SECOND_DIST = atof(row[r++]);
|
||||
MAX_LINK_SPAWN_DIST = atof(row[r++]);
|
||||
int sc = atoi(row[r++]);
|
||||
SPAWN_LINK_TWICE = sc >= 2?true:false;
|
||||
SPAWN_LINK_THRICE = sc >= 3?true:false;
|
||||
mysql_free_result(res);
|
||||
return(true);
|
||||
}
|
||||
|
||||
mysql_free_result(res);
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
|
||||
//this is the path finding portion of the program.
|
||||
|
||||
#include "../common/types.h"
|
||||
#include "../zone/map.h"
|
||||
#include "../common/rdtsc.h"
|
||||
#include "quadtree.h"
|
||||
#include "apathing.h"
|
||||
#include "boostcrap.h"
|
||||
#include <stdio.h>
|
||||
#include <mysql.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
using namespace std;
|
||||
|
||||
void find_path_info(Map *map, PathGraph *big, vector< vector<PathEdge*> > &path_finding) {
|
||||
//make sure our path finding vector is big enough, and all NULL
|
||||
{
|
||||
int size = big->nodes.size();
|
||||
vector<PathEdge*> tmp(size, (PathEdge*)NULL);
|
||||
path_finding.resize(0);
|
||||
path_finding.resize(size, tmp);
|
||||
}
|
||||
|
||||
//take our minimal spanning tree and try to link every single
|
||||
//point in it like spawns are linked. The idea is to create
|
||||
//a large set of alternative paths and cycles.
|
||||
//get our list of nodes.
|
||||
list<PathNode *> all_points = big->nodes;
|
||||
big->nodes.clear();
|
||||
//make our current edge list
|
||||
std::map< pair<PathNode *, PathNode *>, bool > edgelist;
|
||||
list<PathEdge*>::iterator cure,ende,tmp;
|
||||
PathEdge *e;
|
||||
cure = big->edges.begin();
|
||||
ende = big->edges.end();
|
||||
PathNode *from;
|
||||
PathNode *to;
|
||||
for(; cure != ende; cure++) {
|
||||
e = *cure;
|
||||
from = e->from;
|
||||
to = e->to;
|
||||
//hack to make order on the ID not matter
|
||||
if(int32(from) < int32(to)) {
|
||||
PathNode *tmp = from;
|
||||
from = to;
|
||||
to = tmp;
|
||||
}
|
||||
pair<PathNode *, PathNode *> id(from, to);
|
||||
edgelist[id] = true;
|
||||
}
|
||||
//link up the points, making sure not to add edges allready in the MST
|
||||
link_spawns(map, big, all_points, FINAL_LINK_POINTS_DIST, &edgelist);
|
||||
|
||||
//now we have our graph all connected up.
|
||||
|
||||
/*
|
||||
disabled in favor of all-pairs shortest path.
|
||||
|
||||
//get the list of edges leaving each node.
|
||||
std::map<PathNode*, vector<PathEdge*> > node_edges;
|
||||
find_node_edges(big, node_edges);
|
||||
|
||||
//color every node 0
|
||||
list<PathNode *>::iterator curn, endn;
|
||||
curn = big->nodes.begin();
|
||||
endn = big->nodes.end();
|
||||
for(; curn != endn; curn++) {
|
||||
(*curn)->color = 0;
|
||||
}
|
||||
|
||||
//for each node, run a breadth first search from each node to find the
|
||||
//shortest path to each node from each node.
|
||||
*/
|
||||
|
||||
|
||||
//run all pairs shortest path so we have some information to
|
||||
//use for our metric in A*
|
||||
|
||||
//build our boost graph with length weights
|
||||
MyGraph vg(big->nodes.size());
|
||||
property_map<MyGraph, edge_weight_t>::type weightlist;
|
||||
std::map<PathEdge *, EdgeDesc> edgemap;
|
||||
build_boost_graph(vg, weightlist, edgemap, big, true); //set weights to distances
|
||||
|
||||
|
||||
vector< vector<int> > D;
|
||||
vector<int> counts;
|
||||
vector<int> disjoint_counts;
|
||||
vector<int> first_node;
|
||||
|
||||
//color the graph and get us the info we need to do our job (D)
|
||||
color_disjoint_graphs(big, vg, map, NULL, D, counts, disjoint_counts, first_node);
|
||||
|
||||
//figure out what edges link to each node.
|
||||
std::map<PathNode*, vector<PathEdge*> > node_edges;
|
||||
find_node_edges(big, node_edges);
|
||||
|
||||
int cur_node_id;
|
||||
list<PathNode*>::iterator curn,endn;
|
||||
PathNode *n;
|
||||
vector<int>::iterator curp,endp;
|
||||
vector<PathEdge*>::iterator curev,endev;
|
||||
vector<PathEdge *>::iterator cur_res, end_res;
|
||||
|
||||
curn = big->nodes.begin();
|
||||
endn = big->nodes.end();
|
||||
for(; curn != endn; curn++) {
|
||||
n = *curn;
|
||||
|
||||
//get all our vector refs that we need since
|
||||
vector<int> &cv = D[n->node_id]; //distance array
|
||||
vector<PathEdge *> &pf = path_finding[n->node_id]; //result
|
||||
vector<PathEdge *> &el = node_edges[n]; //our edges
|
||||
|
||||
//for each other node, find an edge which gets us closer to that node
|
||||
curp = cv.begin();
|
||||
endp = cv.end();
|
||||
cur_res = pf.begin();
|
||||
end_res = pf.end();
|
||||
for(cur_node_id = 0; curp != endp && cur_res != end_res; curp++, cur_node_id++, cur_res++) {
|
||||
int distance_to_cur_node = *curp;
|
||||
|
||||
//if this ourself, we have no path basically.
|
||||
if(n->node_id == cur_node_id) {
|
||||
*cur_res = NULL;
|
||||
continue;
|
||||
}
|
||||
//if we cant reach them, why look
|
||||
if(distance_to_cur_node == INT_MAX) {
|
||||
*cur_res = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
//see which edge gets us closer
|
||||
int closest = distance_to_cur_node; //start with the distance between this node and the target node, we need something closer
|
||||
PathEdge *best_edge = NULL;
|
||||
curev = el.begin();
|
||||
endev = el.end();
|
||||
for(; curev != endev; curev++) {
|
||||
e = *curev;
|
||||
int this_dist; //distance from the other end of this edge to the current target node
|
||||
if(e->from == n) {
|
||||
if(e->to->node_id == cur_node_id)
|
||||
this_dist = 0; //this is the goal node
|
||||
else
|
||||
this_dist = D[e->to->node_id][cur_node_id];
|
||||
} else { //assume e->to == n
|
||||
if(e->from->node_id == cur_node_id)
|
||||
this_dist = 0; //this is the goal node
|
||||
else
|
||||
this_dist = D[e->from->node_id][cur_node_id];
|
||||
}
|
||||
if(this_dist == 0) {
|
||||
//this will be the best.
|
||||
best_edge = e;
|
||||
break;
|
||||
}
|
||||
if(this_dist == INT_MAX)
|
||||
continue; //not reachable
|
||||
if(this_dist < closest) {
|
||||
closest = this_dist;
|
||||
best_edge = e;
|
||||
}
|
||||
}
|
||||
if(best_edge == NULL) {
|
||||
//unable to find a path...
|
||||
printf("Should have been able to find a path from %d to %d, but couldent.\n", n->node_id, cur_node_id);
|
||||
}
|
||||
*cur_res = best_edge;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
we have our node edges, our all pairs shortest path
|
||||
our graphs, and our MST edge list.
|
||||
now we can run A*
|
||||
|
||||
- our metric should use the all pairs shortest path to
|
||||
determine the value for h.
|
||||
- We want to favor edges from the MST over newly created edges,
|
||||
so we will give such edges a lower weight, maybe by 20%?
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
|
||||
Father Nitwit's Fear Pathing File Maker Thing
|
||||
Copyright (C) 2005 Father Nitwit (eqemu@8ass.com)
|
||||
|
||||
I'll release thisunder the GPL, even though I hate the GPL.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||
are required to give you total support for your newly bought product;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This is a modified quadtree that stores a list of all
|
||||
nodes reachable within a defined distance from the bounds
|
||||
of that quadtree node. Therefor this distance should be
|
||||
the maximum distance used to search for a node.
|
||||
|
||||
In case this distance is not big enough, we also store a
|
||||
complete list of reachable nodes at each quadtree level, so
|
||||
the root has all nodes for sure. This makes it possible to
|
||||
garuntee that we can find A node which is closest.
|
||||
|
||||
*/
|
||||
|
||||
#include "quadtree.h"
|
||||
#include "apathing.h"
|
||||
#include "../zone/map.h"
|
||||
|
||||
//#define SPLIT_DEBUG
|
||||
|
||||
QTNode::QTNode(Map *_map, float dist2, float Tminx, float Tmaxx, float Tminy, float Tmaxy) {
|
||||
node1 = NULL;
|
||||
node2 = NULL;
|
||||
node3 = NULL;
|
||||
node4 = NULL;
|
||||
minx = Tminx;
|
||||
maxx = Tmaxx;
|
||||
miny = Tminy;
|
||||
maxy = Tmaxy;
|
||||
map = _map;
|
||||
search_dist2 = dist2; //this is a distance-squared
|
||||
final = false;
|
||||
buildVertexes();
|
||||
|
||||
}
|
||||
|
||||
QTNode::~QTNode() {
|
||||
clearNodes();
|
||||
}
|
||||
|
||||
void QTNode::clearNodes() {
|
||||
if(node1 != NULL)
|
||||
delete node1;
|
||||
if(node2 != NULL)
|
||||
delete node2;
|
||||
if(node3 != NULL)
|
||||
delete node3;
|
||||
if(node4 != NULL)
|
||||
delete node4;
|
||||
node1 = NULL;
|
||||
node2 = NULL;
|
||||
node3 = NULL;
|
||||
node4 = NULL;
|
||||
}
|
||||
|
||||
|
||||
//assumes that both supplied arrays are big enough per countQTNodes/Facelists
|
||||
void QTNode::fillBlocks(PathTree_Struct *heads, PathPointRef *flist, unsigned long &hindex, unsigned long &findex) {
|
||||
PathTree_Struct *head = &heads[hindex];
|
||||
hindex++;
|
||||
|
||||
head->minx = minx;
|
||||
head->maxx = maxx;
|
||||
head->miny = miny;
|
||||
head->maxy = maxy;
|
||||
head->flags = 0;
|
||||
//printf("Node %u: (%.2f -> %.2f, %.2f -> %.2f)\n", hindex-1, head->minx, head->maxx, head->miny, head->maxy);
|
||||
|
||||
//rearranged to give all QT nodes a node list
|
||||
head->nodelist.count = nodes.size();
|
||||
head->nodelist.offset = findex;
|
||||
//printf(" Final node with %u nodes, list offset %lu.\n", head->nodes.count, head->nodes.offset);
|
||||
list<PathNode *>::iterator curs,end;
|
||||
curs = nodes.begin();
|
||||
end = nodes.end();
|
||||
for(; curs != end; curs++) {
|
||||
//printf("Got to node index %d (0x%x)\n", findex, *curs);
|
||||
PathNode *cur = *curs;
|
||||
flist[findex] = cur->node_id;
|
||||
findex++;
|
||||
}
|
||||
// findex += head->nodes.count;
|
||||
|
||||
|
||||
if(final) {
|
||||
head->flags |= pathNodeFinal;
|
||||
} else {
|
||||
head->flags = 0;
|
||||
//branch node.
|
||||
|
||||
if(node1 != NULL) {
|
||||
head->nodes[0] = hindex;
|
||||
node1->fillBlocks(heads, flist, hindex, findex);
|
||||
} else {
|
||||
head->nodes[0] = PATH_NODE_NONE;
|
||||
}
|
||||
if(node2 != NULL) {
|
||||
head->nodes[1] = hindex;
|
||||
node2->fillBlocks(heads, flist, hindex, findex);
|
||||
} else {
|
||||
head->nodes[1] = PATH_NODE_NONE;
|
||||
}
|
||||
if(node3 != NULL) {
|
||||
head->nodes[2] = hindex;
|
||||
node3->fillBlocks(heads, flist, hindex, findex);
|
||||
} else {
|
||||
head->nodes[2] = PATH_NODE_NONE;
|
||||
}
|
||||
if(node4 != NULL) {
|
||||
head->nodes[3] = hindex;
|
||||
node4->fillBlocks(heads, flist, hindex, findex);
|
||||
} else {
|
||||
head->nodes[3] = PATH_NODE_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long QTNode::countQTNodes() const {
|
||||
unsigned long c = 1;
|
||||
if(node1 != NULL)
|
||||
c += node1->countQTNodes();
|
||||
if(node2 != NULL)
|
||||
c += node2->countQTNodes();
|
||||
if(node3 != NULL)
|
||||
c += node3->countQTNodes();
|
||||
if(node4 != NULL)
|
||||
c += node4->countQTNodes();
|
||||
return(c);
|
||||
}
|
||||
|
||||
/*unsigned long QTNode::countNodes() const {
|
||||
unsigned long c = nodes.size();
|
||||
if(node1 != NULL)
|
||||
c += node1->countNodes();
|
||||
if(node2 != NULL)
|
||||
c += node2->countNodes();
|
||||
if(node3 != NULL)
|
||||
c += node3->countNodes();
|
||||
if(node4 != NULL)
|
||||
c += node4->countNodes();
|
||||
return(c);
|
||||
}*/
|
||||
|
||||
unsigned long QTNode::countPathNodes() const {
|
||||
// unsigned long c = final? nodes.size() : 0;
|
||||
unsigned long c = nodes.size();
|
||||
if(node1 != NULL)
|
||||
c += node1->countPathNodes();
|
||||
if(node2 != NULL)
|
||||
c += node2->countPathNodes();
|
||||
if(node3 != NULL)
|
||||
c += node3->countPathNodes();
|
||||
if(node4 != NULL)
|
||||
c += node4->countPathNodes();
|
||||
return(c);
|
||||
}
|
||||
|
||||
void QTNode::divideYourself(int depth) {
|
||||
// printf("Dividing in box (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d nodes.\n",
|
||||
// minx, maxx, miny, maxy, depth, nodes.size());
|
||||
|
||||
unsigned long cc;
|
||||
cc = nodes.size();
|
||||
#ifdef MAX_QUADRENT_NODES
|
||||
if(cc <= MAX_QUADRENT_NODES) {
|
||||
#ifdef SPLIT_DEBUG
|
||||
printf("Stopping (nodecount) on box (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d nodes.\n",
|
||||
minx, maxx, miny, maxy, depth, cc);
|
||||
#endif
|
||||
final = true;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MIN_QUADRENT_SIZE
|
||||
if((maxx - minx) < MIN_QUADRENT_SIZE || (maxy - miny) < MIN_QUADRENT_SIZE) {
|
||||
#ifdef SPLIT_DEBUG
|
||||
printf("Stopping on box (size) (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d nodes.\n",
|
||||
minx, maxx, miny, maxy, depth, cc);
|
||||
#endif
|
||||
final = true;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
doSplit();
|
||||
|
||||
//get counts on our split
|
||||
float c1, c2, c3, c4;
|
||||
c1 = node1? node1->nodes.size() : 0;
|
||||
c2 = node2? node2->nodes.size() : 0;
|
||||
c3 = node3? node3->nodes.size() : 0;
|
||||
c4 = node4? node4->nodes.size() : 0;
|
||||
|
||||
#ifdef MIN_QUADRENT_GAIN
|
||||
int miss = 0;
|
||||
float gain1 = 1.0 - c1 / cc;
|
||||
float gain2 = 1.0 - c2 / cc;
|
||||
float gain3 = 1.0 - c3 / cc;
|
||||
float gain4 = 1.0 - c4 / cc;
|
||||
|
||||
//see how many missed the gain mark
|
||||
if(gain1 < MIN_QUADRENT_GAIN)
|
||||
miss++;
|
||||
if(gain2 < MIN_QUADRENT_GAIN)
|
||||
miss++;
|
||||
if(gain3 < MIN_QUADRENT_GAIN)
|
||||
miss++;
|
||||
if(gain4 < MIN_QUADRENT_GAIN)
|
||||
miss++;
|
||||
|
||||
if(miss > MAX_QUADRENT_MISSES) {
|
||||
#ifdef SPLIT_DEBUG
|
||||
printf("Stopping (gain) on box (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d nodes.\n",
|
||||
minx, maxx, miny, maxy, depth, cc);
|
||||
#endif
|
||||
final = true;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
//if all nodes pass through all quadrents, then we are done
|
||||
//partially obsoleted by gain test.
|
||||
if(c1 == c2 && c1 == c3 && c1 == c4) {
|
||||
#ifdef SPLIT_DEBUG
|
||||
printf("Stopping (empty) on box (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d nodes.\n",
|
||||
minx, maxx, miny, maxy, depth, cc);
|
||||
printf("Our counts: %.3f, %.3f, %.3f, %.3f\n", c1, c2, c3, c4);
|
||||
#endif
|
||||
final = true;
|
||||
return;
|
||||
}
|
||||
|
||||
//there are prolly some more intelligent stopping criteria...
|
||||
|
||||
depth++;
|
||||
|
||||
if(node1 != NULL)
|
||||
node1->divideYourself(depth);
|
||||
if(node2 != NULL)
|
||||
node2->divideYourself(depth);
|
||||
if(node3 != NULL)
|
||||
node3->divideYourself(depth);
|
||||
if(node4 != NULL)
|
||||
node4->divideYourself(depth);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void QTNode::buildVertexes() {
|
||||
|
||||
v[0].x = v[1].x = v[2].x = v[3].x = minx;
|
||||
v[4].x = v[5].x = v[6].x = v[7].x = maxx;
|
||||
|
||||
v[0].y = v[1].y = v[4].y = v[5].y = miny;
|
||||
v[2].y = v[3].y = v[6].y = v[7].y = maxy;
|
||||
|
||||
v[0].z = v[3].z = v[4].z = v[7].z = -999999;
|
||||
v[1].z = v[2].z = v[5].z = v[6].z = 9999999;
|
||||
}
|
||||
|
||||
bool QTNode::IsInNode(const QTNode *n, const PathNode *o) {
|
||||
//printf("IIN: (%.3f,%.3f) in (%.3f -> %.3f, %.3f -> %.3f)\n", o->x, o->y, n->minx, n->maxx, n->miny, n->maxy);
|
||||
if( o->x >= n->minx && o->x < n->maxx
|
||||
&& o->y >= n->miny && o->y < n->maxy )
|
||||
return(true);
|
||||
|
||||
//well its not inside the node, so see if it is reachable from it
|
||||
|
||||
//4 points of this node
|
||||
GPoint pt1(n->minx, n->miny, 0),
|
||||
pt2(n->minx, n->maxy, 0),
|
||||
pt3(n->maxx, n->miny, 0),
|
||||
pt4(n->maxx, n->maxy, 0);
|
||||
if( o->Dist2(&pt1) < search_dist2
|
||||
|| o->Dist2(&pt2) < search_dist2
|
||||
|| o->Dist2(&pt3) < search_dist2
|
||||
|| o->Dist2(&pt4) < search_dist2)
|
||||
return(true);
|
||||
|
||||
//not inside, and not reachable...
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
void QTNode::doSplit() {
|
||||
|
||||
|
||||
//find midpoints...
|
||||
float midx = minx + (maxx - minx) / 2.0;
|
||||
float midy = miny + (maxy - miny) / 2.0;
|
||||
|
||||
//ordering following definitions in map.h
|
||||
node1 = new QTNode(map, search_dist2, midx, maxx, midy, maxy);
|
||||
node2 = new QTNode(map, search_dist2, minx, midx, midy, maxy);
|
||||
node3 = new QTNode(map, search_dist2, minx, midx, miny, midy);
|
||||
node4 = new QTNode(map, search_dist2, midx, maxx, miny, midy);
|
||||
if(node1 == NULL || node2 == NULL || node3 == NULL || node4 == NULL) {
|
||||
printf("Error: unable to allocate new QTNode, giving up.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// unsigned long l;
|
||||
// l = faces.size();
|
||||
// for(r = 0; r < l; r++) {
|
||||
// PathNode *cur = faces[r];
|
||||
list<PathNode *>::iterator curs,end;
|
||||
curs = nodes.begin();
|
||||
end = nodes.end();
|
||||
for(; curs != end; curs++) {
|
||||
PathNode *cur = *curs;
|
||||
if(IsInNode(node1, cur))
|
||||
node1->nodes.push_back(cur);
|
||||
if(IsInNode(node2, cur))
|
||||
node2->nodes.push_back(cur);
|
||||
if(IsInNode(node3, cur))
|
||||
node3->nodes.push_back(cur);
|
||||
if(IsInNode(node4, cur))
|
||||
node4->nodes.push_back(cur);
|
||||
}
|
||||
|
||||
//clean up empty sets.
|
||||
if(node1->nodes.size() == 0) {
|
||||
delete node1;
|
||||
node1 = NULL;
|
||||
}
|
||||
if(node2->nodes.size() == 0) {
|
||||
delete node2;
|
||||
node2 = NULL;
|
||||
}
|
||||
if(node3->nodes.size() == 0) {
|
||||
delete node3;
|
||||
node3 = NULL;
|
||||
}
|
||||
if(node4->nodes.size() == 0) {
|
||||
delete node4;
|
||||
node4 = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
#ifndef FPQUADTREE_H
|
||||
#define FPQUADTREE_H
|
||||
|
||||
//pull in datatypes from zone's map.h
|
||||
#include "../zone/pathing.h"
|
||||
#include "gpoint.h"
|
||||
|
||||
/*
|
||||
|
||||
Father Nitwit's Fear Pathing File Maker Thing
|
||||
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
using namespace std;
|
||||
|
||||
|
||||
#define COUNT_MACTHES 1
|
||||
|
||||
//this is the version number to put in the map header
|
||||
#undef PATHFILE_VERSION //override this from fearpath.h with our version
|
||||
#define PATHFILE_VERSION 0x02000000
|
||||
|
||||
//quadtree stopping criteria, comment any to disable them
|
||||
//you want to keep the nodes small since the fear space is very sparse
|
||||
#define MAX_QUADRENT_NODES 4 //if box has fewer than this, stop
|
||||
#define MIN_QUADRENT_SIZE 50.0f //if box has a dimention smaller than this, stop
|
||||
#define MIN_QUADRENT_GAIN 0.1f //minimum split ratio before stopping
|
||||
#define MAX_QUADRENT_MISSES 2 //maximum number of quads which can miss their gains
|
||||
//1 or 2 make sense, others are less useful
|
||||
|
||||
class PathNode;
|
||||
class Map;
|
||||
|
||||
//quadtree node container
|
||||
class QTNode {
|
||||
public:
|
||||
QTNode(Map *_map, float _dist2, float Tminx, float Tmaxx, float Tminy, float Tmaxy);
|
||||
~QTNode();
|
||||
|
||||
void clearNodes();
|
||||
|
||||
void doSplit();
|
||||
void divideYourself(int depth);
|
||||
|
||||
void buildVertexes();
|
||||
|
||||
unsigned long countQTNodes() const; //counts QT nodes
|
||||
unsigned long countPathNodes() const; //counts PathNodes
|
||||
|
||||
void fillBlocks(PathTree_Struct *heads, PathPointRef *flist, unsigned long &hindex, unsigned long &findex);
|
||||
|
||||
float minx;
|
||||
float miny;
|
||||
float maxx;
|
||||
float maxy;
|
||||
unsigned long nnodes;
|
||||
list<PathNode *> nodes;
|
||||
|
||||
bool IsInNode(const QTNode *n, const PathNode *o);
|
||||
|
||||
/*
|
||||
quadrent definitions:
|
||||
quad 1 (node1):
|
||||
x>=0, y>=0
|
||||
quad 2 (node2):
|
||||
x<0, y>=0
|
||||
quad 3 (node3):
|
||||
x<0, y<0
|
||||
quad 4 (node4):
|
||||
x>=0, y<0
|
||||
*/
|
||||
QTNode *node1;
|
||||
QTNode *node2;
|
||||
QTNode *node3;
|
||||
QTNode *node4;
|
||||
GPoint v[8];
|
||||
bool final;
|
||||
|
||||
Map *map;
|
||||
float search_dist2;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
|
||||
#shortnames update...
|
||||
#echo "SELECT short_name FROM zone" | ~/eqemu/runmysql -B|grep -v _ >shortnames
|
||||
|
||||
mkdir -p images
|
||||
mkdir -p output
|
||||
|
||||
for f in `cat shortnames`
|
||||
do
|
||||
if [ -r "$f.path" ]; then
|
||||
echo "We allready have $f's path file. Remove it to rebuild"
|
||||
continue;
|
||||
fi
|
||||
|
||||
echo
|
||||
echo
|
||||
echo "********************************************************************************"
|
||||
echo "Building $f"
|
||||
|
||||
./apath "$f" | tee "output/$f.txt"
|
||||
mv "paths-${f}-mstree.png" images/
|
||||
done
|
||||
@@ -0,0 +1,131 @@
|
||||
airplane
|
||||
akanon
|
||||
arena
|
||||
arena2
|
||||
befallen
|
||||
beholder
|
||||
blackburrow
|
||||
burningwood
|
||||
butcher
|
||||
cabeast
|
||||
cabwest
|
||||
cauldron
|
||||
cazicthule
|
||||
charasis
|
||||
chardok
|
||||
citymist
|
||||
cobaltscar
|
||||
commons
|
||||
crushbone
|
||||
crystal
|
||||
cshome
|
||||
dalnir
|
||||
dreadlands
|
||||
droga
|
||||
eastkarana
|
||||
eastwastes
|
||||
ecommons
|
||||
emeraldjungle
|
||||
erudnext
|
||||
erudnint
|
||||
erudsxing
|
||||
everfrost
|
||||
fearplane
|
||||
feerrott
|
||||
felwithea
|
||||
felwitheb
|
||||
fieldofbone
|
||||
firiona
|
||||
freporte
|
||||
freportn
|
||||
freportw
|
||||
frontiermtns
|
||||
frozenshadow
|
||||
gfaydark
|
||||
greatdivide
|
||||
growthplane
|
||||
gukbottom
|
||||
gukta
|
||||
guktop
|
||||
halas
|
||||
hateplane
|
||||
hateplaneb
|
||||
highkeep
|
||||
highpass
|
||||
hole
|
||||
iceclad
|
||||
innothule
|
||||
jaggedpine
|
||||
kael
|
||||
kaesora
|
||||
kaladima
|
||||
kaladimb
|
||||
karnor
|
||||
kedge
|
||||
kerraridge
|
||||
kithicor
|
||||
kurn
|
||||
lakeofillomen
|
||||
lakerathe
|
||||
lavastorm
|
||||
lfaydark
|
||||
load
|
||||
load2
|
||||
mischiefplane
|
||||
mistmoore
|
||||
misty
|
||||
najena
|
||||
necropolis
|
||||
nektulos
|
||||
neriaka
|
||||
neriakb
|
||||
neriakc
|
||||
northkarana
|
||||
nro
|
||||
nurga
|
||||
oasis
|
||||
oggok
|
||||
oot
|
||||
overthere
|
||||
paineel
|
||||
paw
|
||||
permafrost
|
||||
qcat
|
||||
qey2hh1
|
||||
qeynos
|
||||
qeynos2
|
||||
qeytoqrg
|
||||
qrg
|
||||
rathemtn
|
||||
rivervale
|
||||
runnyeye
|
||||
sebilis
|
||||
sirens
|
||||
skyfire
|
||||
skyshrine
|
||||
sleeper
|
||||
soldunga
|
||||
soldungb
|
||||
soltemple
|
||||
southkarana
|
||||
sro
|
||||
steamfont
|
||||
stonebrunt
|
||||
swampofnohope
|
||||
templeveeshan
|
||||
thurgadina
|
||||
thurgadinb
|
||||
timorous
|
||||
tox
|
||||
trakanon
|
||||
tutorial
|
||||
tutoriala
|
||||
tutorialb
|
||||
unrest
|
||||
veeshan
|
||||
veksar
|
||||
velketor
|
||||
wakening
|
||||
warrens
|
||||
warslikswood
|
||||
westwastes
|
||||
Reference in New Issue
Block a user