You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
408 lines
8.3 KiB
408 lines
8.3 KiB
/* |
|
* |
|
* 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; either version 2 of the License, or (at |
|
* your option) any later version. |
|
* |
|
* This program is distributed in the hope that it will be useful, but |
|
* WITHOUT ANY WARRANTY; 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 |
|
* |
|
* In addition, as a special exception, the author gives permission to |
|
* link the code of this program with the Half-Life Game Engine ("HL |
|
* Engine") and Modified Game Libraries ("MODs") developed by Valve, |
|
* L.L.C ("Valve"). You must obey the GNU General Public License in all |
|
* respects for all of the code used other than the HL Engine and MODs |
|
* from Valve. If you modify this file, you may extend this exception |
|
* to your version of the file, but you are not obligated to do so. If |
|
* you do not wish to do so, delete this exception statement from your |
|
* version. |
|
* |
|
*/ |
|
|
|
#ifndef NAV_H |
|
#define NAV_H |
|
#ifdef _WIN32 |
|
#pragma once |
|
#endif |
|
|
|
// STL uses exceptions, but we are not compiling with them - ignore warning |
|
#pragma warning(disable : 4530) |
|
|
|
// to help identify nav files |
|
#define NAV_MAGIC_NUMBER 0xFEEDFACE |
|
|
|
// A place is a named group of navigation areas |
|
typedef unsigned int Place; |
|
|
|
// ie: "no place" |
|
#define UNDEFINED_PLACE 0 |
|
#define ANY_PLACE 0xFFFF |
|
|
|
#define WALK_THRU_DOORS 0x01 |
|
#define WALK_THRU_BREAKABLES 0x02 |
|
#define WALK_THRU_EVERYTHING (WALK_THRU_DOORS | WALK_THRU_BREAKABLES) |
|
|
|
enum NavErrorType |
|
{ |
|
NAV_OK, |
|
NAV_CANT_ACCESS_FILE, |
|
NAV_INVALID_FILE, |
|
NAV_BAD_FILE_VERSION, |
|
NAV_CORRUPT_DATA, |
|
}; |
|
|
|
enum NavAttributeType |
|
{ |
|
NAV_CROUCH = 0x01, // must crouch to use this node/area |
|
NAV_JUMP = 0x02, // must jump to traverse this area |
|
NAV_PRECISE = 0x04, // do not adjust for obstacles, just move along area |
|
NAV_NO_JUMP = 0x08, // inhibit discontinuity jumping |
|
}; |
|
|
|
enum NavDirType |
|
{ |
|
NORTH = 0, |
|
EAST, |
|
SOUTH, |
|
WEST, |
|
|
|
NUM_DIRECTIONS |
|
}; |
|
|
|
// Defines possible ways to move from one area to another |
|
enum NavTraverseType |
|
{ |
|
// NOTE: First 4 directions MUST match NavDirType |
|
GO_NORTH = 0, |
|
GO_EAST, |
|
GO_SOUTH, |
|
GO_WEST, |
|
GO_LADDER_UP, |
|
GO_LADDER_DOWN, |
|
GO_JUMP, |
|
|
|
NUM_TRAVERSE_TYPES |
|
}; |
|
|
|
enum NavCornerType |
|
{ |
|
NORTH_WEST = 0, |
|
NORTH_EAST, |
|
SOUTH_EAST, |
|
SOUTH_WEST, |
|
|
|
NUM_CORNERS |
|
}; |
|
|
|
enum NavRelativeDirType |
|
{ |
|
FORWARD = 0, |
|
RIGHT, |
|
BACKWARD, |
|
LEFT, |
|
UP, |
|
DOWN, |
|
|
|
NUM_RELATIVE_DIRECTIONS |
|
}; |
|
|
|
const float GenerationStepSize = 25.0f; // (30) was 20, but bots can't fit always fit |
|
const float StepHeight = 18.0f; // if delta Z is greater than this, we have to jump to get up |
|
const float JumpHeight = 41.8f; // if delta Z is less than this, we can jump up on it |
|
const float JumpCrouchHeight = 58.0f; // (48) if delta Z is less than or equal to this, we can jumpcrouch up on it |
|
|
|
// Strictly speaking, you CAN get up a slope of 1.643 (about 59 degrees), but you move very, very slowly |
|
// This slope will represent the slope you can navigate without much slowdown |
|
// rise/run - if greater than this, we can't move up it (de_survivor canyon ramps) |
|
const float MaxSlope = 1.4f; |
|
|
|
// instead of MaxSlope, we are using the following max Z component of a unit normal |
|
const float MaxUnitZSlope = 0.7f; |
|
|
|
const float BotRadius = 10.0f; // circular extent that contains bot |
|
const float DeathDrop = 200.0f; // (300) distance at which we will die if we fall - should be about 600, and pay attention to fall damage during pathfind |
|
|
|
const float HalfHumanWidth = 16.0f; |
|
const float HalfHumanHeight = 36.0f; |
|
const float HumanHeight = 72.0f; |
|
|
|
struct Extent |
|
{ |
|
Vector lo; |
|
Vector hi; |
|
|
|
float SizeX() const { return hi.x - lo.x; } |
|
float SizeY() const { return hi.y - lo.y;} |
|
float SizeZ() const { return hi.z - lo.z; } |
|
float Area() const { return SizeX() * SizeY(); } |
|
|
|
// return true if 'pos' is inside of this extent |
|
bool Contains(const Vector *pos) const |
|
{ |
|
return (pos->x >= lo.x && pos->x <= hi.x && |
|
pos->y >= lo.y && pos->y <= hi.y && |
|
pos->z >= lo.z && pos->z <= hi.z); |
|
} |
|
}; |
|
|
|
struct Ray |
|
{ |
|
Vector from; |
|
Vector to; |
|
}; |
|
|
|
inline NavDirType OppositeDirection(NavDirType dir) |
|
{ |
|
switch (dir) |
|
{ |
|
case NORTH: |
|
return SOUTH; |
|
case EAST: |
|
return WEST; |
|
case SOUTH: |
|
return NORTH; |
|
case WEST: |
|
return EAST; |
|
} |
|
|
|
return NORTH; |
|
} |
|
|
|
inline NavDirType DirectionLeft(NavDirType dir) |
|
{ |
|
switch (dir) |
|
{ |
|
case NORTH: |
|
return WEST; |
|
case SOUTH: |
|
return EAST; |
|
case EAST: |
|
return NORTH; |
|
case WEST: |
|
return SOUTH; |
|
} |
|
|
|
return NORTH; |
|
} |
|
|
|
inline NavDirType DirectionRight(NavDirType dir) |
|
{ |
|
switch (dir) |
|
{ |
|
case NORTH: |
|
return EAST; |
|
case SOUTH: |
|
return WEST; |
|
case EAST: |
|
return SOUTH; |
|
case WEST: |
|
return NORTH; |
|
} |
|
|
|
return NORTH; |
|
} |
|
|
|
inline void AddDirectionVector(Vector *v, NavDirType dir, float amount) |
|
{ |
|
switch (dir) |
|
{ |
|
case NORTH: |
|
v->y -= amount; |
|
return; |
|
case SOUTH: |
|
v->y += amount; |
|
return; |
|
case EAST: |
|
v->x += amount; |
|
return; |
|
case WEST: |
|
v->x -= amount; |
|
return; |
|
} |
|
} |
|
|
|
inline float DirectionToAngle(NavDirType dir) |
|
{ |
|
switch (dir) |
|
{ |
|
case NORTH: |
|
return 270.0f; |
|
case EAST: |
|
return 0.0f; |
|
case SOUTH: |
|
return 90.0f; |
|
case WEST: |
|
return 180.0f; |
|
} |
|
|
|
return 0.0f; |
|
} |
|
|
|
inline NavDirType AngleToDirection(float angle) |
|
{ |
|
while (angle < 0.0f) |
|
angle += 360.0f; |
|
|
|
while (angle > 360.0f) |
|
angle -= 360.0f; |
|
|
|
if (angle < 45 || angle > 315) |
|
return EAST; |
|
|
|
if (angle >= 45 && angle < 135) |
|
return SOUTH; |
|
|
|
if (angle >= 135 && angle < 225) |
|
return WEST; |
|
|
|
return NORTH; |
|
} |
|
|
|
inline void DirectionToVector2D(NavDirType dir, Vector2D *v) |
|
{ |
|
switch (dir) |
|
{ |
|
case NORTH: |
|
v->x = 0.0f; |
|
v->y = -1.0f; |
|
break; |
|
case SOUTH: |
|
v->x = 0.0f; |
|
v->y = 1.0f; |
|
break; |
|
case EAST: |
|
v->x = 1.0f; |
|
v->y = 0.0f; |
|
break; |
|
case WEST: |
|
v->x = -1.0f; |
|
v->y = 0.0f; |
|
break; |
|
} |
|
} |
|
|
|
inline void SnapToGrid(Vector *pos) |
|
{ |
|
int cx = pos->x / GenerationStepSize; |
|
int cy = pos->y / GenerationStepSize; |
|
pos->x = cx * GenerationStepSize; |
|
pos->y = cy * GenerationStepSize; |
|
} |
|
|
|
inline void SnapToGrid(float *value) |
|
{ |
|
int c = *value / GenerationStepSize; |
|
*value = c * GenerationStepSize; |
|
} |
|
|
|
// custom |
|
inline float SnapToGrid(float value) |
|
{ |
|
int c = value / GenerationStepSize; |
|
return c * GenerationStepSize; |
|
} |
|
|
|
inline float NormalizeAngle(float angle) |
|
{ |
|
while (angle < -180.0f) |
|
angle += 360.0f; |
|
|
|
while (angle > 180.0f) |
|
angle -= 360.0f; |
|
|
|
return angle; |
|
} |
|
|
|
inline float NormalizeAnglePositive(float angle) |
|
{ |
|
while (angle < 0.0f) |
|
angle += 360.0f; |
|
|
|
while (angle >= 360.0f) |
|
angle -= 360.0f; |
|
|
|
return angle; |
|
} |
|
|
|
inline float AngleDifference(float a, float b) |
|
{ |
|
float angleDiff = a - b; |
|
|
|
while (angleDiff > 180.0f) |
|
angleDiff -= 360.0f; |
|
|
|
while (angleDiff < -180.0f) |
|
angleDiff += 360.0f; |
|
|
|
return angleDiff; |
|
} |
|
|
|
inline bool AnglesAreEqual(float a, float b, float tolerance = 5.0f) |
|
{ |
|
if (abs(int64(AngleDifference(a, b))) < tolerance) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
inline bool VectorsAreEqual(const Vector *a, const Vector *b, float tolerance = 0.1f) |
|
{ |
|
if (abs(a->x - b->x) < tolerance && |
|
abs(a->y - b->y) < tolerance && |
|
abs(a->z - b->z) < tolerance) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
inline bool IsEntityWalkable(entvars_t *entity, unsigned int flags) |
|
{ |
|
// if we hit a door, assume its walkable because it will open when we touch it |
|
if (FClassnameIs(entity, "func_door") || FClassnameIs(entity, "func_door_rotating")) |
|
return (flags & WALK_THRU_DOORS) ? true : false; |
|
|
|
// if we hit a breakable object, assume its walkable because we will shoot it when we touch it |
|
if (FClassnameIs(entity, "func_breakable") && entity->takedamage == DAMAGE_YES) |
|
return (flags & WALK_THRU_BREAKABLES) ? true : false; |
|
|
|
return false; |
|
} |
|
|
|
// Check LOS, ignoring any entities that we can walk through |
|
|
|
inline bool IsWalkableTraceLineClear(Vector &from, Vector &to, unsigned int flags = 0) |
|
{ |
|
TraceResult result; |
|
edict_t *ignore = NULL; |
|
Vector useFrom = from; |
|
|
|
while (true) |
|
{ |
|
UTIL_TraceLine(useFrom, to, ignore_monsters, ignore, &result); |
|
|
|
if (result.flFraction != 1.0f && IsEntityWalkable(VARS(result.pHit), flags)) |
|
{ |
|
ignore = result.pHit; |
|
|
|
Vector dir = to - from; |
|
dir.NormalizeInPlace(); |
|
useFrom = result.vecEndPos + 5.0f * dir; |
|
} |
|
else |
|
break; |
|
} |
|
|
|
if (result.flFraction == 1.0f) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
#endif // NAV_H
|
|
|