svn -> git Migration

This commit is contained in:
KimLS
2013-02-16 16:14:39 -08:00
parent 88c9715fb0
commit da7347f76f
1174 changed files with 445622 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+591
View File
@@ -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);
}
+290
View File
@@ -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
+759
View File
@@ -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);
}
}
}
}
+32
View File
@@ -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
+113
View File
@@ -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;
}
+49
View File
@@ -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
+276
View File
@@ -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);
}
+212
View File
@@ -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%?
*/
}
+366
View File
@@ -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;
}
}
+88
View File
@@ -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
+23
View File
@@ -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
+131
View File
@@ -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