float Map::FindClosestZ(VERTEX p ) const { // Unlike FindBestZ, this method finds the closest Z value above or below the specified point. // std::list<float> ZSet; NodeRef NodeR = SeekNode(MAP_ROOT_NODE, p.x, p.y); if( NodeR == NODE_NONE || NodeR >= m_Nodes) return 0; PNODE CurrentNode = &mNodes[NodeR]; if(!(CurrentNode->flags & nodeFinal)) return 0; VERTEX p1(p), p2(p), Result; p1.z = 999999; p2.z = BEST_Z_INVALID; const uint32 *CurrentFaceList = mFaceLists + CurrentNode->faces.offset; for(unsigned long i = 0; i < CurrentNode->faces.count; ++i) { if(*CurrentFaceList > m_Faces) continue; PFACE CurrentFace = &mFinalFaces[ *CurrentFaceList ]; if(CurrentFace->nz > 0 && LineIntersectsFace(CurrentFace, p1, p2, &Result)) ZSet.push_back(Result.z); CurrentFaceList++; } if(ZSet.size() == 0) return 0; if(ZSet.size() == 1) return ZSet.front(); float ClosestZ = -999999; for(list<float>::iterator Iterator = ZSet.begin(); Iterator != ZSet.end(); ++Iterator) { if(ABS(p.z - (*Iterator)) < ABS(p.z - ClosestZ)) ClosestZ = (*Iterator); } return ClosestZ; }
PNODE Map::SeekNode( PNODE _node, float x, float y ) { if( !_node ) return NULL; if( x>= _node->minx && x<= _node->maxx && y>= _node->miny && y<= _node->maxy ) { if( _node->mask ) { PNODE tmp; tmp = SeekNode( _node->node1, x, y ); if( tmp ) return tmp; tmp = SeekNode( _node->node2, x, y ); if( tmp ) return tmp; tmp = SeekNode( _node->node3, x, y ); if( tmp ) return tmp; tmp = SeekNode( _node->node4, x, y ); if( tmp ) return tmp; } else return _node; } return NULL; }
bool Map::LineIntersectsZoneNoZLeaps(VERTEX start, VERTEX end, float step_mag, VERTEX *result, FACE **on) { float z = -999999; VERTEX step; VERTEX cur; cur.x = start.x; cur.y = start.y; cur.z = start.z; step.x = end.x - start.x; step.y = end.y - start.y; step.z = end.z - start.z; float factor = step_mag / sqrt(step.x*step.x + step.y*step.y + step.z*step.z); step.x *= factor; step.y *= factor; step.z *= factor; int steps = 0; if (step.x > 0 && step.x < 0.001f) step.x = 0.001f; if (step.y > 0 && step.y < 0.001f) step.y = 0.001f; if (step.z > 0 && step.z < 0.001f) step.z = 0.001f; if (step.x < 0 && step.x > -0.001f) step.x = -0.001f; if (step.y < 0 && step.y > -0.001f) step.y = -0.001f; if (step.z < 0 && step.z > -0.001f) step.z = -0.001f; NodeRef cnode, lnode; lnode = 0; //while we are not past end //always do this once, even if start == end. while(cur.x != end.x || cur.y != end.y || cur.z != end.z) { steps++; cnode = SeekNode(GetRoot(), cur.x, cur.y); if (cnode == NODE_NONE) { return(true); } VERTEX me; me.x = cur.x; me.y = cur.y; me.z = cur.z; VERTEX hit; FACE *onhit; float best_z = zone->zonemap->FindBestZ(cnode, me, &hit, &onhit); float diff = ABS(best_z-z); // diff *= sign(diff); if (z == -999999 || best_z == -999999 || diff < 12.0) z = best_z; else return(true); //look at current location if(cnode != NODE_NONE && cnode != lnode) { if(LineIntersectsNode(cnode, start, end, result, on)) { return(true); } lnode = cnode; } //move 1 step if (cur.x != end.x) cur.x += step.x; if (cur.y != end.y) cur.y += step.y; if (cur.z != end.z) cur.z += step.z; //watch for end conditions if ( (cur.x > end.x && end.x >= start.x) || (cur.x < end.x && end.x <= start.x) || (step.x == 0) ) { cur.x = end.x; } if ( (cur.y > end.y && end.y >= start.y) || (cur.y < end.y && end.y <= start.y) || (step.y == 0) ) { cur.y = end.y; } if ( (cur.z > end.z && end.z >= start.z) || (cur.z < end.z && end.z < start.z) || (step.z == 0) ) { cur.z = end.z; } } //walked entire line and didnt run into anything... return(false); }
float Map::FindBestZ( NodeRef node_r, VERTEX p1, VERTEX *result, FACE **on) const { _ZP(Map_FindBestZ); p1.z += RuleI(Map, FindBestZHeightAdjust); if(RuleB(Map, UseClosestZ)) return FindClosestZ(p1); if(node_r == GetRoot()) { node_r = SeekNode(node_r, p1.x, p1.y); } if( node_r == NODE_NONE || node_r >= m_Nodes) { return(BEST_Z_INVALID); } const PNODE _node = &mNodes[node_r]; if(!(_node->flags & nodeFinal)) { return(BEST_Z_INVALID); //not a final node... could find the proper node... } VERTEX tmp_result; //dummy placeholder if they do not ask for a result. if(result == NULL) result = &tmp_result; VERTEX p2(p1); p2.z = BEST_Z_INVALID; float best_z = BEST_Z_INVALID; int zAttempt; unsigned long i; PFACE cur; // If we don't find a bestZ on the first attempt, we try again from a position CurrentZ + 10 higher // This is in case the pathing between waypoints temporarily sends the NPC below ground level. // for(zAttempt=1; zAttempt<=2; zAttempt++) { const uint32 *cfl = mFaceLists + _node->faces.offset; #ifdef DEBUG_BEST_Z printf("Start finding best Z...\n"); #endif for(i = 0; i < _node->faces.count; i++) { if(*cfl > m_Faces) continue; //watch for invalid lists, they seem to happen, e.g. in eastwastes.map cur = &mFinalFaces[ *cfl ]; //printf("Intersecting with face %lu\n", *cfl); if(LineIntersectsFace(cur, p1, p2, result)) { #ifdef DEBUG_BEST_Z printf(" %lu (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)\n", *cfl, cur->a.x, cur->a.y, cur->a.z, cur->b.x, cur->b.y, cur->b.z, cur->c.x, cur->c.y, cur->c.z); printf("Found a z: %.2f\n", result->z); #endif if (result->z > best_z) { if(on != NULL) *on = cur; best_z = result->z; } } cfl++; } if(best_z != BEST_Z_INVALID) return best_z; p1.z = p1.z + 10 ; // If we can't find a best Z, the NPC is probably just under the world. Try again from 10 units higher up. } #ifdef DEBUG_BEST_Z fflush(stdout); printf("Best Z found: %.2f\n", best_z); #endif return best_z; }
bool Map::LineIntersectsZone(VERTEX start, VERTEX end, float step_mag, VERTEX *result, FACE **on) const { _ZP(Map_LineIntersectsZone); // Cast a ray from start to end, checking for collisions in each node between the two points. // float stepx, stepy, stepz, curx, cury, curz; curx = start.x; cury = start.y; curz = start.z; VERTEX cur = start; stepx = end.x - start.x; stepy = end.y - start.y; stepz = end.z - start.z; if((stepx == 0) && (stepy == 0) && (stepz == 0)) return false; float factor = sqrt(stepx*stepx + stepy*stepy + stepz*stepz); stepx = (stepx/factor)*step_mag; stepy = (stepy/factor)*step_mag; stepz = (stepz/factor)*step_mag; NodeRef cnode, lnode, finalnode; lnode = NODE_NONE; //last node visited cnode = SeekNode(GetRoot(), start.x, start.y); finalnode = SeekNode(GetRoot(), end.x, end.y); if(cnode == finalnode) return LineIntersectsNode(cnode, start, end, result, on); do { stepx = (float)end.x - curx; stepy = (float)end.y - cury; stepz = (float)end.z - curz; factor = sqrt(stepx*stepx + stepy*stepy + stepz*stepz); stepx = (stepx/factor)*step_mag; stepy = (stepy/factor)*step_mag; stepz = (stepz/factor)*step_mag; cnode = SeekNode(GetRoot(), curx, cury); if(cnode != lnode) { lnode = cnode; if(cnode == NODE_NONE) return false; if(LineIntersectsNode(cnode, start, end, result, on)) return(true); if(cnode == finalnode) return false; } curx += stepx; cury += stepy; curz += stepz; cur.x = curx; cur.y = cury; cur.z = curz; if(ABS(curx - end.x) < step_mag) cur.x = end.x; if(ABS(cury - end.y) < step_mag) cur.y = end.y; if(ABS(curz - end.z) < step_mag) cur.z = end.z; } while(cur.x != end.x || cur.y != end.y || cur.z != end.z); return false; }
NodeRef Map::SeekNode( NodeRef node_r, float x, float y ) const { if(node_r == NODE_NONE || node_r >= m_Nodes) { return(NODE_NONE); } PNODE _node = &mNodes[node_r]; #ifdef DEBUG_SEEK printf("Seeking node for %u:(%.2f, %.2f) with root 0x%x.\n", node_r, x, y, _node); printf(" Current Box: (%.2f -> %.2f, %.2f -> %.2f)\n", _node->minx, _node->maxx, _node->miny, _node->maxy); #endif if( x>= _node->minx && x<= _node->maxx && y>= _node->miny && y<= _node->maxy ) { if( _node->flags & nodeFinal ) { #ifdef DEBUG_SEEK printf("Seeking node for %u:(%.2f, %.2f) with root 0x%x.\n", node_r, x, y, _node); printf(" Final Node: (%.2f -> %.2f, %.2f -> %.2f)\n", _node->minx, _node->maxx, _node->miny, _node->maxy); fflush(stdout); printf(" Final node found with %d faces.\n", _node->faces.count); /*printf(" Faces:\n"); unsigned long *cfl = mFaceLists + _node->faces.offset; unsigned long m; for(m = 0; m < _node->faces.count; m++) { FACE *c = &mFinalFaces[ *cfl ]; printf(" %lu (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)\n", *cfl, c->a.x, c->a.y, c->a.z, c->b.x, c->b.y, c->b.z, c->c.x, c->c.y, c->c.z); cfl++; }*/ #endif return node_r; } #ifdef DEBUG_SEEK printf(" Kids: %u, %u, %u, %u\n", _node->nodes[0], _node->nodes[1], _node->nodes[2], _node->nodes[3]); printf(" Contained In Box: (%.2f -> %.2f, %.2f -> %.2f)\n", _node->minx, _node->maxx, _node->miny, _node->maxy); /*printf(" Node found has children.\n"); if(_node->node1 != NULL) { printf("\tNode: (%.2f -> %.2f, %.2f -> %.2f)\n", _node->node1->minx, _node->node1->maxx, _node->node1->miny, _node->node1->maxy); } if(_node->node2 != NULL) { printf("\tNode: (%.2f -> %.2f, %.2f -> %.2f)\n", _node->node2->minx, _node->node2->maxx, _node->node2->miny, _node->node2->maxy); } if(_node->node3 != NULL) { printf("\tNode: (%.2f -> %.2f, %.2f -> %.2f)\n", _node->node3->minx, _node->node3->maxx, _node->node3->miny, _node->node3->maxy); } if(_node->node4 != NULL) { printf("\tNode: (%.2f -> %.2f, %.2f -> %.2f)\n", _node->node4->minx, _node->node4->maxx, _node->node4->miny, _node->node4->maxy); }*/ #endif //NOTE: could precalc these and store them in node headers NodeRef tmp = NODE_NONE; #ifdef OPTIMIZE_QT_LOOKUPS float midx = _node->minx + (_node->maxx - _node->minx) * 0.5; float midy = _node->miny + (_node->maxy - _node->miny) * 0.5; //follow ordering rules from map.h... if(x < midx) { if(y < midy) { //quad 3 if(_node->nodes[2] != NODE_NONE && _node->nodes[2] != node_r) tmp = SeekNode( _node->nodes[2], x, y ); } else { //quad 2 if(_node->nodes[2] != NODE_NONE && _node->nodes[1] != node_r) tmp = SeekNode( _node->nodes[1], x, y ); } } else { if(y < midy) { //quad 4 if(_node->nodes[2] != NODE_NONE && _node->nodes[3] != node_r) tmp = SeekNode( _node->nodes[3], x, y ); } else { //quad 1 if(_node->nodes[2] != NODE_NONE && _node->nodes[0] != node_r) tmp = SeekNode( _node->nodes[0], x, y ); } } if( tmp != NODE_NONE ) return tmp; #else if(_node->nodes[0] == node_r) return(NODE_NONE); //prevent infinite recursion tmp = SeekNode( _node->nodes[0], x, y ); if( tmp != NODE_NONE ) return tmp; if(_node->nodes[1] == node_r) return(NODE_NONE); //prevent infinite recursion tmp = SeekNode( _node->nodes[1], x, y ); if( tmp != NODE_NONE ) return tmp; if(_node->nodes[2] == node_r) return(NODE_NONE); //prevent infinite recursion tmp = SeekNode( _node->nodes[2], x, y ); if( tmp != NODE_NONE ) return tmp; if(_node->nodes[3] == node_r) return(NODE_NONE); //prevent infinite recursion tmp = SeekNode( _node->nodes[3], x, y ); if( tmp != NODE_NONE ) return tmp; #endif } #ifdef DEBUG_SEEK printf(" No node found.\n"); #endif return(NODE_NONE); }