1055 lines
29 KiB
C++
1055 lines
29 KiB
C++
// Copyright (C) 2007 Id Software, Inc.
|
|
//
|
|
|
|
#include "../../precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "../../Game_local.h" // for cvars and debug drawing
|
|
#include "../../Player.h"
|
|
#include "../../Misc.h"
|
|
#include "../../ContentMask.h"
|
|
#include "../../botai/BotThreadData.h"
|
|
|
|
#include "AAS_local.h"
|
|
#include "AASCallback_FindCoverArea.h"
|
|
#include "AASCallback_FindFlaggedArea.h"
|
|
#include "ObstacleAvoidance.h"
|
|
|
|
/*
|
|
============
|
|
idAASLocal::DrawCone
|
|
============
|
|
*/
|
|
void idAASLocal::DrawCone( const idVec3 &origin, const idVec3 &dir, float radius, const idVec4 &color ) const {
|
|
int i;
|
|
idMat3 axis;
|
|
idVec3 center, top, p, lastp;
|
|
|
|
axis[2] = dir;
|
|
axis[2].NormalVectors( axis[0], axis[1] );
|
|
axis[1] = -axis[1];
|
|
|
|
center = origin + dir;
|
|
top = center + dir * (3.0f * radius);
|
|
lastp = center + radius * axis[1];
|
|
|
|
for ( i = 20; i <= 360; i += 20 ) {
|
|
p = center + sin( DEG2RAD(i) ) * radius * axis[0] + cos( DEG2RAD(i) ) * radius * axis[1];
|
|
gameRenderWorld->DebugLine( color, lastp, p, 0 );
|
|
gameRenderWorld->DebugLine( color, p, top, 0 );
|
|
lastp = p;
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::DrawReachability
|
|
============
|
|
*/
|
|
void idAASLocal::DrawReachability( const aasReachability_t *reach, const char *name ) const {
|
|
gameRenderWorld->DebugArrow( colorCyan, reach->GetStart(), reach->GetEnd(), 2, 0 );
|
|
|
|
if ( name != NULL && name[0] != '\0' && gameLocal.GetLocalPlayer() != NULL ) {
|
|
idStr flags;
|
|
if ( reach->travelFlags & TFL_INVALID ) flags.Append( " invalid" );
|
|
if ( reach->travelFlags & TFL_INVALID_GDF ) flags.Append( " invalidgdf" );
|
|
if ( reach->travelFlags & TFL_INVALID_STROGG ) flags.Append( " invalidstrogg" );
|
|
if ( reach->travelFlags & TFL_AIR ) flags.Append( " air" );
|
|
if ( reach->travelFlags & TFL_WATER ) flags.Append( " water" );
|
|
if ( reach->travelFlags & TFL_WALK ) flags.Append( " walk" );
|
|
if ( reach->travelFlags & TFL_WALKOFFLEDGE ) flags.Append( " walkoffledge" );
|
|
if ( reach->travelFlags & TFL_WALKOFFBARRIER ) flags.Append( " walloffbarrier" );
|
|
if ( reach->travelFlags & TFL_BARRIERJUMP ) flags.Append( " barrierjump" );
|
|
if ( reach->travelFlags & TFL_JUMP ) flags.Append( " jump" );
|
|
if ( reach->travelFlags & TFL_LADDER ) flags.Append( " ladder" );
|
|
if ( reach->travelFlags & TFL_SWIM ) flags.Append( " swim" );
|
|
if ( reach->travelFlags & TFL_WATERJUMP ) flags.Append( " waterjump" );
|
|
if ( reach->travelFlags & TFL_TELEPORT ) flags.Append( " teleport" );
|
|
if ( reach->travelFlags & TFL_ELEVATOR ) flags.Append( " elevator" );
|
|
gameRenderWorld->DrawText( va( "%s\nTravel Flags:%s", name, flags.c_str() ), ( reach->GetStart() + reach->GetEnd() ) * 0.5f, 0.1f, colorWhite, gameLocal.GetLocalPlayer()->GetViewAxis(), 1, 0 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::DrawEdge
|
|
============
|
|
*/
|
|
void idAASLocal::DrawEdge( int edgeNum, bool arrow ) const {
|
|
const aasEdge_t *edge;
|
|
|
|
if ( !file ) {
|
|
return;
|
|
}
|
|
|
|
edge = &file->GetEdge( edgeNum );
|
|
if ( arrow ) {
|
|
gameRenderWorld->DebugArrow( colorRed, file->GetVertex( edge->vertexNum[0] ), file->GetVertex( edge->vertexNum[1] ), 1, 0 );
|
|
} else {
|
|
gameRenderWorld->DebugLine( colorRed, file->GetVertex( edge->vertexNum[0] ), file->GetVertex( edge->vertexNum[1] ), 0 );
|
|
}
|
|
|
|
if ( aas_showEdgeNums.GetBool() && gameLocal.GetLocalPlayer() ) {
|
|
gameRenderWorld->DrawText( va( "%d", edgeNum ), ( file->GetVertex( edge->vertexNum[0] ) + file->GetVertex( edge->vertexNum[1] ) ) * 0.5f + idVec3(0,0,4), 0.1f, colorRed, gameLocal.GetLocalPlayer()->GetViewAxis(), 1, 0 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::DrawArea
|
|
============
|
|
*/
|
|
void idAASLocal::DrawArea( int areaNum ) const {
|
|
int i, j, numEdges, firstEdge;
|
|
idVec3 mid, end;
|
|
const aasArea_t *area;
|
|
const aasReachability_t *reach;
|
|
|
|
if ( !file ) {
|
|
return;
|
|
}
|
|
|
|
area = &file->GetArea( areaNum );
|
|
numEdges = area->numEdges;
|
|
firstEdge = area->firstEdge;
|
|
|
|
mid.Zero();
|
|
for ( i = 0; i < numEdges; i++ ) {
|
|
DrawEdge( abs( file->GetEdgeIndex( firstEdge + i ) ), true );
|
|
j = file->GetEdgeIndex( firstEdge + i );
|
|
mid += file->GetVertex( file->GetEdge( abs( j ) ).vertexNum[ j < 0 ] );
|
|
}
|
|
|
|
mid /= numEdges;
|
|
end = mid + 5.0f * file->GetSettings().invGravityDir;
|
|
gameRenderWorld->DebugArrow( colorGreen, mid, end, 1, 0 );
|
|
|
|
for ( reach = area->reach; reach; reach = reach->next ) {
|
|
DrawReachability( reach, NULL );
|
|
}
|
|
|
|
gameRenderWorld->DrawText( va( "%d", areaNum ), AreaCenter( areaNum ) + idVec3( 0.0f, 0.0f, 4.0f ), 0.1f, colorWhite, gameLocal.GetLocalPlayer()->GetViewAxis(), 1, 0 );
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::DefaultSearchBounds
|
|
============
|
|
*/
|
|
const idBounds &idAASLocal::DefaultSearchBounds( void ) const {
|
|
return file->GetSettings().boundingBox;
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::TravelFlagForTeam
|
|
============
|
|
*/
|
|
int idAASLocal::TravelFlagForTeam( void ) const {
|
|
idPlayer *player = gameLocal.GetLocalPlayer();
|
|
if ( player == NULL || player->GetGameTeam() == NULL ) {
|
|
return TFL_VALID_GDF_AND_STROGG;
|
|
}
|
|
switch( player->GetGameTeam()->GetBotTeam() ) {
|
|
case GDF: return TFL_VALID_GDF;
|
|
case STROGG: return TFL_VALID_STROGG;
|
|
}
|
|
return TFL_VALID_GDF_AND_STROGG;
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::TravelFlagWalkForTeam
|
|
============
|
|
*/
|
|
int idAASLocal::TravelFlagWalkForTeam( void ) const {
|
|
idPlayer *player = gameLocal.GetLocalPlayer();
|
|
if ( player == NULL || player->GetGameTeam() == NULL ) {
|
|
return TFL_VALID_WALK_GDF_AND_STROGG;
|
|
}
|
|
switch( player->GetGameTeam()->GetBotTeam() ) {
|
|
case GDF: return TFL_VALID_WALK_GDF;
|
|
case STROGG: return TFL_VALID_WALK_STROGG;
|
|
}
|
|
return TFL_VALID_WALK_GDF_AND_STROGG;
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::TravelFlagInvalidForTeam
|
|
============
|
|
*/
|
|
int idAASLocal::TravelFlagInvalidForTeam( void ) const {
|
|
idPlayer *player = gameLocal.GetLocalPlayer();
|
|
if ( player == NULL || player->GetGameTeam() == NULL ) {
|
|
return TFL_INVALID|TFL_INVALID_GDF|TFL_INVALID_STROGG;
|
|
}
|
|
switch( player->GetGameTeam()->GetBotTeam() ) {
|
|
case GDF: return TFL_INVALID|TFL_INVALID_GDF;
|
|
case STROGG: return TFL_INVALID|TFL_INVALID_STROGG;
|
|
}
|
|
return TFL_INVALID|TFL_INVALID_GDF|TFL_INVALID_STROGG;
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::ShowArea
|
|
============
|
|
*/
|
|
void idAASLocal::ShowArea( const idVec3 &origin, int mode ) const {
|
|
static int lastAreaNum;
|
|
int areaNum;
|
|
const aasArea_t *area;
|
|
idVec3 org;
|
|
|
|
org = origin;
|
|
if ( mode == 1 ) {
|
|
areaNum = PointReachableAreaNum( origin, DefaultSearchBounds(), AAS_AREA_REACHABLE_WALK, TravelFlagInvalidForTeam() );
|
|
PushPointIntoArea( areaNum, org );
|
|
} else {
|
|
areaNum = PointAreaNum( origin );
|
|
}
|
|
|
|
if ( aas_showTravelTime.GetInteger() ) {
|
|
int travelTime;
|
|
const aasReachability_t *reach;
|
|
|
|
RouteToGoalArea( areaNum, org, aas_showTravelTime.GetInteger(), TFL_WALK|TFL_AIR, travelTime, &reach );
|
|
gameLocal.Printf( "\rtt = %4d", travelTime );
|
|
if ( reach ) {
|
|
gameLocal.Printf( " to area %4d", reach->toAreaNum );
|
|
DrawArea( reach->toAreaNum );
|
|
}
|
|
}
|
|
|
|
if ( areaNum != lastAreaNum ) {
|
|
area = &file->GetArea( areaNum );
|
|
gameLocal.Printf( "area %d:", areaNum );
|
|
if ( area->flags & AAS_AREA_LEDGE ) {
|
|
gameLocal.Printf( " ledge" );
|
|
}
|
|
if ( area->flags & AAS_AREA_CONTENTS_CLUSTERPORTAL ) {
|
|
gameLocal.Printf( " clusterportal" );
|
|
}
|
|
if ( area->flags & AAS_AREA_CONTENTS_OBSTACLE ) {
|
|
gameLocal.Printf( " obstacle" );
|
|
}
|
|
if ( area->flags & AAS_AREA_OUTSIDE ) {
|
|
gameLocal.Printf( " outside" );
|
|
}
|
|
if ( area->flags & AAS_AREA_HIGH_CEILING ) {
|
|
gameLocal.Printf( " highceiling" );
|
|
}
|
|
if ( area->travelFlags & ( TFL_INVALID | TFL_INVALID_GDF | TFL_INVALID_STROGG ) ) {
|
|
gameLocal.Printf( " /" );
|
|
if ( area->travelFlags & TFL_INVALID ) {
|
|
gameLocal.Printf( " invalid" );
|
|
}
|
|
if ( area->travelFlags & TFL_INVALID_GDF ) {
|
|
gameLocal.Printf( " invalidgdf" );
|
|
}
|
|
if ( area->travelFlags & TFL_INVALID_STROGG ) {
|
|
gameLocal.Printf( " invalidstrogg" );
|
|
}
|
|
}
|
|
gameLocal.Printf( "\n" );
|
|
lastAreaNum = areaNum;
|
|
}
|
|
|
|
if ( org != origin ) {
|
|
idBounds bnds = file->GetSettings().boundingBox;
|
|
bnds[ 1 ].z = bnds[ 0 ].z;
|
|
gameRenderWorld->DebugBounds( colorYellow, bnds, org );
|
|
gameRenderWorld->DebugArrow( colorYellow, origin, org, 1 );
|
|
}
|
|
|
|
DrawArea( areaNum );
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::ShowWalkPath
|
|
============
|
|
*/
|
|
void idAASLocal::ShowWalkPath( int startAreaNum, const idVec3 &startOrigin, int goalAreaNum, const idVec3 &goalOrigin, int travelFlags, int walkTravelFlags ) const {
|
|
|
|
if ( file == NULL ) {
|
|
return;
|
|
}
|
|
|
|
int curAreaNum = startAreaNum;
|
|
|
|
idVec3 org = startOrigin;
|
|
for ( int i = 0; i < 100; i++ ) {
|
|
|
|
int travelTime;
|
|
const aasReachability_t *reach;
|
|
|
|
if ( !RouteToGoalArea( curAreaNum, org, goalAreaNum, travelFlags, travelTime, &reach ) ) {
|
|
break;
|
|
}
|
|
|
|
if ( !reach ) {
|
|
break;
|
|
}
|
|
|
|
gameRenderWorld->DebugArrow( colorGreen, org, reach->GetStart(), 2, 0 );
|
|
DrawReachability( reach, NULL );
|
|
if ( aas_showAreas.GetBool() ) {
|
|
DrawArea( curAreaNum );
|
|
}
|
|
|
|
if ( reach->toAreaNum == goalAreaNum ) {
|
|
break;
|
|
}
|
|
|
|
curAreaNum = reach->toAreaNum;
|
|
org = reach->GetEnd();
|
|
}
|
|
|
|
idAASPath path;
|
|
if ( !WalkPathToGoal( path, startAreaNum, startOrigin, goalAreaNum, goalOrigin, travelFlags, walkTravelFlags ) ) {
|
|
return;
|
|
}
|
|
|
|
gameRenderWorld->DebugArrow( colorBlue, startOrigin, path.moveGoal, 2, 0 );
|
|
|
|
// idObstacleAvoidance::obstaclePath_t obstaclePath;
|
|
// idObstacleAvoidance obstacleAvoidance;
|
|
// botThreadData.BuildObstacleList( obstacleAvoidance, startOrigin, startAreaNum, false );
|
|
// obstacleAvoidance.FindPathAroundObstacles( file->GetSettings().boundingBox, file->GetSettings().obstaclePVSRadius, this, startOrigin, path.moveGoal, obstaclePath );
|
|
|
|
// if ( obstaclePath.firstObstacle != idObstacleAvoidance::OBSTACLE_ID_INVALID ) {
|
|
// path.moveGoal = obstaclePath.seekPos;
|
|
// gameRenderWorld->DebugArrow( colorOrange, startOrigin, path.moveGoal, 2, 0 );
|
|
// }
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::ShowWalkPath
|
|
============
|
|
*/
|
|
void idAASLocal::ShowWalkPath( const idVec3 &startOrigin, int goalAreaNum, const idVec3 &goalOrigin ) const {
|
|
|
|
if ( file == NULL ) {
|
|
return;
|
|
}
|
|
|
|
idPlayer *player = gameLocal.GetLocalPlayer();
|
|
if ( player == NULL ) {
|
|
return;
|
|
}
|
|
|
|
idVec3 areaOrigin = startOrigin;
|
|
int startAreaNum = PointReachableAreaNum( areaOrigin, DefaultSearchBounds(), AAS_AREA_REACHABLE_WALK, TravelFlagInvalidForTeam() );
|
|
PushPointIntoArea( startAreaNum, areaOrigin );
|
|
|
|
ShowWalkPath( startAreaNum, areaOrigin, goalAreaNum, goalOrigin, TravelFlagForTeam(), TravelFlagWalkForTeam() );
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::ShowHopPath
|
|
============
|
|
*/
|
|
void idAASLocal::ShowHopPath( int startAreaNum, const idVec3 &startOrigin, int goalAreaNum, const idVec3 &goalOrigin, int travelFlags, int walkTravelFlags, const idAASHopPathParms &parms ) const {
|
|
|
|
if ( file == NULL ) {
|
|
return;
|
|
}
|
|
|
|
int curAreaNum = startAreaNum;
|
|
|
|
idVec3 org = startOrigin;
|
|
for ( int i = 0; i < 100; i++ ) {
|
|
|
|
int travelTime;
|
|
const aasReachability_t *reach;
|
|
|
|
if ( !RouteToGoalArea( curAreaNum, org, goalAreaNum, travelFlags, travelTime, &reach ) ) {
|
|
break;
|
|
}
|
|
|
|
if ( !reach ) {
|
|
break;
|
|
}
|
|
|
|
gameRenderWorld->DebugArrow( colorGreen, org, reach->GetStart(), 2, 0 );
|
|
DrawReachability( reach, NULL );
|
|
if ( aas_showAreas.GetBool() ) {
|
|
DrawArea( curAreaNum );
|
|
}
|
|
|
|
if ( reach->toAreaNum == goalAreaNum ) {
|
|
break;
|
|
}
|
|
|
|
curAreaNum = reach->toAreaNum;
|
|
org = reach->GetEnd();
|
|
}
|
|
|
|
idAASPath path;
|
|
if ( WalkPathToGoal( path, startAreaNum, startOrigin, goalAreaNum, goalOrigin, travelFlags, walkTravelFlags ) ) {
|
|
gameRenderWorld->DebugArrow( colorBlue, startOrigin, path.moveGoal, 2 );
|
|
if ( ExtendHopPathToGoal( path, startAreaNum, startOrigin, goalAreaNum, goalOrigin, travelFlags, walkTravelFlags, parms ) ) {
|
|
gameRenderWorld->DebugArrow( colorCyan, startOrigin, path.moveGoal, 2 );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::ShowHopPath
|
|
============
|
|
*/
|
|
void idAASLocal::ShowHopPath( const idVec3 &startOrigin, int goalAreaNum, const idVec3 &goalOrigin ) const {
|
|
|
|
if ( file == NULL ) {
|
|
return;
|
|
}
|
|
|
|
idVec3 areaOrigin = startOrigin;
|
|
int startAreaNum = PointReachableAreaNum( areaOrigin, DefaultSearchBounds(), AAS_AREA_REACHABLE_WALK, TravelFlagInvalidForTeam() );
|
|
PushPointIntoArea( startAreaNum, areaOrigin );
|
|
|
|
ShowHopPath( startAreaNum, areaOrigin, goalAreaNum, goalOrigin, TravelFlagForTeam(), TravelFlagWalkForTeam(), idAASHopPathParms() );
|
|
}
|
|
|
|
/*
|
|
============
|
|
ProjectTopDown
|
|
============
|
|
*/
|
|
void ProjectTopDown( idVec3 &point, const idVec3 &viewOrigin, const idMat3 &viewAxis, const idMat3 &playerAxis, float distance ) {
|
|
point = ( point - viewOrigin ) * playerAxis;
|
|
point = viewOrigin + distance * viewAxis[0] + point.y * viewAxis[1] + point.x * viewAxis[2];
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::ShowWallEdges
|
|
============
|
|
*/
|
|
void idAASLocal::ShowWallEdges( const idVec3 &origin, int mode, bool showNumbers ) const {
|
|
const int MAX_WALL_EDGES = 1024;
|
|
int edges[MAX_WALL_EDGES];
|
|
float textSize;
|
|
|
|
idPlayer *player = gameLocal.GetLocalPlayer();
|
|
if ( player == NULL ) {
|
|
return;
|
|
}
|
|
|
|
idMat3 viewAxis = player->GetViewAxis();
|
|
idVec3 viewOrigin = player->GetViewPos();
|
|
idMat3 playerAxis = idAngles( 0.0f, -player->GetViewAngles().yaw, 0.0f ).ToMat3();
|
|
|
|
if ( mode == 3 ) {
|
|
textSize = 0.2f;
|
|
} else {
|
|
textSize = 0.1f;
|
|
}
|
|
|
|
float radius = file->GetSettings().obstaclePVSRadius;
|
|
|
|
int areaNum = PointReachableAreaNum( origin, DefaultSearchBounds(), AAS_AREA_REACHABLE_WALK, TravelFlagInvalidForTeam() );
|
|
//int numEdges = GetWallEdges( areaNum, idBounds( origin ).Expand( radius ), TFL_WALK, 64.0f, edges, MAX_WALL_EDGES );
|
|
int numEdges = GetObstaclePVSWallEdges( areaNum, edges, MAX_WALL_EDGES );
|
|
|
|
// move the wall edges to the start of the list
|
|
int numWallEdges = 0;
|
|
for ( int i = 0; i < numEdges; i++ ) {
|
|
if ( ( file->GetEdge( abs( edges[i] ) ).flags & AAS_EDGE_WALL ) != 0 ) {
|
|
idSwap( edges[numWallEdges++], edges[i] );
|
|
}
|
|
}
|
|
|
|
for ( int i = 0; i < numEdges; i++ ) {
|
|
idVec3 start, end;
|
|
|
|
GetEdge( edges[i], start, end );
|
|
|
|
if ( mode == 2 ) {
|
|
|
|
start.z = end.z = origin.z;
|
|
|
|
} else if ( mode == 3 ) {
|
|
|
|
ProjectTopDown( start, viewOrigin, viewAxis, playerAxis, radius * 2.0f );
|
|
ProjectTopDown( end, viewOrigin, viewAxis, playerAxis, radius * 2.0f );
|
|
}
|
|
|
|
if ( ( file->GetEdge( abs( edges[i] ) ).flags & AAS_EDGE_WALL ) != 0 ) {
|
|
gameRenderWorld->DebugLine( colorRed, start, end, 0 );
|
|
} else {
|
|
gameRenderWorld->DebugLine( colorGreen, start, end, 0 );
|
|
}
|
|
if ( showNumbers ) {
|
|
gameRenderWorld->DrawText( va( "%d", edges[i] ), ( start + end ) * 0.5f, textSize, colorWhite, viewAxis, 1, 0 );
|
|
}
|
|
}
|
|
|
|
if ( mode == 3 ) {
|
|
idVec3 box[7] = { origin, origin, origin, origin, origin, origin, origin };
|
|
|
|
box[0][0] += radius;
|
|
box[0][1] += radius;
|
|
|
|
box[1][0] += radius;
|
|
box[1][1] -= radius;
|
|
|
|
box[2][0] -= radius;
|
|
box[2][1] -= radius;
|
|
|
|
box[3][0] -= radius;
|
|
box[3][1] += radius;
|
|
|
|
box[4][1] += radius;
|
|
|
|
box[5][0] += radius * 0.1f;
|
|
box[5][1] += radius - radius * 0.1f;
|
|
|
|
box[6][0] -= radius * 0.1f;
|
|
box[6][1] += radius - radius * 0.1f;
|
|
|
|
for ( int i = 0; i < 7; i++ ) {
|
|
ProjectTopDown( box[i], viewOrigin, viewAxis, playerAxis, radius * 2.0f );
|
|
}
|
|
for ( int i = 0; i < 4; i++ ) {
|
|
gameRenderWorld->DebugLine( colorCyan, box[i], box[(i+1)&3], 0 );
|
|
}
|
|
gameRenderWorld->DebugLine( colorCyan, box[4], box[5], 0 );
|
|
gameRenderWorld->DebugLine( colorCyan, box[4], box[6], 0 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::ShowNearestCoverArea
|
|
============
|
|
*/
|
|
void idAASLocal::ShowNearestCoverArea( const idVec3 &origin, int targetAreaNum ) const {
|
|
int areaNum = PointReachableAreaNum( origin, DefaultSearchBounds(), AAS_AREA_REACHABLE_WALK, TravelFlagInvalidForTeam() );
|
|
idVec3 target = AreaCenter( targetAreaNum );
|
|
|
|
DrawCone( target, idVec3(0,0,1), 16.0f, colorYellow );
|
|
|
|
idAASCallback_FindCoverArea findCover( target );
|
|
idAASGoal goal;
|
|
if ( FindNearestGoal( goal, areaNum, origin, TravelFlagInvalidForTeam(), findCover ) ) {
|
|
DrawArea( goal.areaNum );
|
|
ShowWalkPath( areaNum, origin, goal.areaNum, goal.origin, TravelFlagForTeam(), TravelFlagWalkForTeam() );
|
|
DrawCone( goal.origin, idVec3( 0, 0, 1 ), 16.0f, colorWhite );
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::ShowNearestInsideArea
|
|
============
|
|
*/
|
|
void idAASLocal::ShowNearestInsideArea( const idVec3 &origin ) const {
|
|
int areaNum = PointReachableAreaNum( origin, DefaultSearchBounds(), AAS_AREA_REACHABLE_WALK, TravelFlagInvalidForTeam() );
|
|
|
|
idAASCallback_FindFlaggedArea findInside( AAS_AREA_OUTSIDE, false );
|
|
idAASGoal goal;
|
|
if ( FindNearestGoal( goal, areaNum, origin, TravelFlagForTeam(), findInside ) ) {
|
|
DrawArea( goal.areaNum );
|
|
ShowWalkPath( areaNum, origin, goal.areaNum, goal.origin, TravelFlagForTeam(), TravelFlagWalkForTeam() );
|
|
DrawCone( goal.origin, idVec3( 0, 0, 1 ), 16.0f, colorWhite );
|
|
}
|
|
}
|
|
|
|
struct idPullPlayerState {
|
|
idPullPlayerState() {
|
|
jumpNow = false;
|
|
ladderDir = vec3_zero;
|
|
ladderTime = 0;
|
|
}
|
|
|
|
bool jumpNow;
|
|
idVec3 ladderDir;
|
|
int ladderTime;
|
|
} pullPlayerState;
|
|
|
|
/*
|
|
============
|
|
idAASLocal::PullPlayer
|
|
============
|
|
*/
|
|
bool idAASLocal::PullPlayer( const idVec3 &origin, int goalAreaNum, const idVec3 &goalOrigin, int &startAreaNum, int &travelTime ) const {
|
|
|
|
startAreaNum = 0;
|
|
travelTime = 0;
|
|
|
|
idPlayer *player = gameLocal.GetLocalPlayer();
|
|
if ( player == NULL ) {
|
|
return true;
|
|
}
|
|
|
|
if ( goalAreaNum == 0 ) {
|
|
return false;
|
|
}
|
|
|
|
if ( player->GetNoClip() ) {
|
|
player->aasPullPlayer = false;
|
|
return false;
|
|
}
|
|
|
|
idVec3 dir = goalOrigin - origin;
|
|
float height = idMath::Fabs( dir.z );
|
|
float dist = dir.ToVec2().Length();
|
|
|
|
if ( dist < 32.0f && height < 128.0f ) {
|
|
return false;
|
|
}
|
|
|
|
idVec3 org = origin;
|
|
startAreaNum = PointReachableAreaNum( org, DefaultSearchBounds(), AAS_AREA_REACHABLE_WALK, TravelFlagInvalidForTeam() );
|
|
PushPointIntoArea( startAreaNum, org );
|
|
|
|
const aasReachability_t *reach;
|
|
RouteToGoalArea( startAreaNum, org, goalAreaNum, TravelFlagForTeam(), travelTime, &reach );
|
|
|
|
ShowWalkPath( startAreaNum, org, goalAreaNum, goalOrigin, TravelFlagForTeam(), TravelFlagWalkForTeam() );
|
|
|
|
idAASPath path;
|
|
if ( !WalkPathToGoal( path, startAreaNum, org, goalAreaNum, goalOrigin, TravelFlagForTeam(), TravelFlagWalkForTeam() ) ) {
|
|
return false;
|
|
}
|
|
|
|
idObstacleAvoidance::obstaclePath_t obstaclePath;
|
|
idObstacleAvoidance obstacleAvoidance;
|
|
botThreadData.BuildObstacleList( obstacleAvoidance, org, startAreaNum, false );
|
|
obstacleAvoidance.FindPathAroundObstacles( file->GetSettings().boundingBox, file->GetSettings().obstaclePVSRadius, this, org, path.moveGoal, obstaclePath );
|
|
path.moveGoal = obstaclePath.seekPos;
|
|
|
|
player->aasPullPlayer = true;
|
|
|
|
usercmd_t usercmd;
|
|
memset( &usercmd, 0, sizeof( usercmd ) );
|
|
|
|
usercmd.forwardmove = 127;
|
|
usercmd.buttons.btn.run = true;
|
|
usercmd.buttons.btn.sprint = false;
|
|
|
|
idVec3 moveDir = path.moveGoal - org;
|
|
idVec3 horizontalDir( moveDir.x, moveDir.y, 0.0f );
|
|
float horizontalDist = horizontalDir.Normalize();
|
|
moveDir.Normalize();
|
|
|
|
switch( path.type ) {
|
|
case PATHTYPE_WALKOFFLEDGE:
|
|
case PATHTYPE_WALKOFFBARRIER: {
|
|
if ( horizontalDist < 80.0f ) {
|
|
usercmd.buttons.btn.run = false;
|
|
usercmd.forwardmove = 16 + horizontalDist;
|
|
}
|
|
break;
|
|
}
|
|
case PATHTYPE_BARRIERJUMP:
|
|
case PATHTYPE_JUMP: {
|
|
if ( horizontalDist < 24.0f ) {
|
|
pullPlayerState.jumpNow = !pullPlayerState.jumpNow;
|
|
if ( pullPlayerState.jumpNow ) {
|
|
usercmd.upmove = 127;
|
|
}
|
|
}
|
|
if ( player->GetPlayerPhysics().IsGrounded() ) {
|
|
if ( horizontalDist < 100.0f ) {
|
|
usercmd.buttons.btn.run = false;
|
|
usercmd.forwardmove = 8 + horizontalDist;
|
|
}
|
|
} else {
|
|
moveDir = path.reachability->GetEnd() - org;
|
|
moveDir.Normalize();
|
|
}
|
|
break;
|
|
}
|
|
case PATHTYPE_LADDER: {
|
|
pullPlayerState.ladderDir = moveDir;
|
|
pullPlayerState.ladderTime = gameLocal.time;
|
|
#if 0
|
|
// test ladder physics code
|
|
if ( !player->GetPlayerPhysics().IsGrounded() ) {
|
|
trace_t trace;
|
|
bool onLadder = false;
|
|
if ( gameLocal.clip.Translation( CLIP_DEBUG_PARMS_CLIENTINFO( self ) trace, org, org + horizontalDir * 32.0f, NULL, mat3_identity, MASK_PLAYERSOLID, player ) ) {
|
|
onLadder = ( gameLocal.entities[ trace.c.entityNum ]->Cast< sdLadderEntity >() != NULL );
|
|
}
|
|
if ( onLadder && !player->GetPlayerPhysics().OnLadder() ) {
|
|
usercmd.forwardmove = 0;
|
|
}
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( player->GetPlayerPhysics().OnLadder() || pullPlayerState.ladderTime > gameLocal.time - 500 ) {
|
|
moveDir = pullPlayerState.ladderDir;
|
|
}
|
|
|
|
idAngles viewAngles = moveDir.ToAngles();
|
|
|
|
player->Move( usercmd, viewAngles );
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::UnFreezePlayer
|
|
============
|
|
*/
|
|
void idAASLocal::UnFreezePlayer() const {
|
|
idPlayer *player = gameLocal.GetLocalPlayer();
|
|
if ( player == NULL ) {
|
|
return;
|
|
}
|
|
player->aasPullPlayer = false;
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::RandomPullPlayer
|
|
============
|
|
*/
|
|
void idAASLocal::RandomPullPlayer( const idVec3 &origin, int mode ) const {
|
|
int areaNum, travelTime;
|
|
static int bestTravelTime = 0;
|
|
static int startAreaNum = 0;
|
|
static int goalAreaNum = 0;
|
|
static int failedCount;
|
|
|
|
if ( goalAreaNum > file->GetNumAreas() ) {
|
|
goalAreaNum = 0;
|
|
}
|
|
|
|
if ( !PullPlayer( origin, goalAreaNum, AreaCenter( goalAreaNum ), areaNum, travelTime ) ) {
|
|
|
|
startAreaNum = 0;
|
|
bestTravelTime = INT_MAX;
|
|
|
|
int rnd = idMath::Ftoi( gameLocal.random.RandomFloat() * file->GetNumAreas() );
|
|
|
|
for ( int i = 0; i < file->GetNumAreas(); i++ ) {
|
|
int n = ( rnd + i ) % file->GetNumAreas();
|
|
if ( ( file->GetArea( n ).flags & AAS_AREA_REACHABLE_WALK ) != 0 ) {
|
|
goalAreaNum = n;
|
|
}
|
|
}
|
|
} else if ( startAreaNum == 0 ) {
|
|
startAreaNum = areaNum;
|
|
bestTravelTime = travelTime;
|
|
}
|
|
|
|
if ( travelTime < bestTravelTime ) {
|
|
bestTravelTime = travelTime;
|
|
failedCount = 0;
|
|
} else {
|
|
failedCount++;
|
|
}
|
|
|
|
if ( failedCount > 10 * 1000 / USERCMD_MSEC ) {
|
|
failedCount = 0;
|
|
if ( mode > 1 ) {
|
|
common->Warning( "failed to go from area %d to area %d at area %d", startAreaNum, goalAreaNum, areaNum );
|
|
goalAreaNum = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::ShowPushIntoArea
|
|
============
|
|
*/
|
|
void idAASLocal::ShowPushIntoArea( const idVec3 &origin ) const {
|
|
int areaNum;
|
|
idVec3 target;
|
|
|
|
target = origin;
|
|
areaNum = PointReachableAreaNum( target, DefaultSearchBounds(), AAS_AREA_REACHABLE_WALK, TravelFlagInvalidForTeam() );
|
|
PushPointIntoArea( areaNum, target );
|
|
gameRenderWorld->DebugArrow( colorGreen, origin, target, 1, 0 );
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::ShowFloorTrace
|
|
============
|
|
*/
|
|
void idAASLocal::ShowFloorTrace( const idVec3 &origin ) const {
|
|
idPlayer *player;
|
|
|
|
player = gameLocal.GetLocalPlayer();
|
|
if ( !player ) {
|
|
return;
|
|
}
|
|
idMat3 playerAxis = idAngles( 0.0f, player->GetViewAngles().yaw, 0.0f ).ToMat3();
|
|
|
|
idVec3 org = origin;
|
|
int areaNum = PointReachableAreaNum( org, DefaultSearchBounds(), AAS_AREA_REACHABLE_WALK, TravelFlagInvalidForTeam() );
|
|
PushPointIntoArea( areaNum, org );
|
|
|
|
aasTraceFloor_t trace;
|
|
TraceFloor( trace, org, areaNum, origin + playerAxis[0] * 1024, TFL_WALK|TFL_AIR );
|
|
|
|
gameRenderWorld->DebugArrow( colorCyan, org, trace.endpos, 1, 0 );
|
|
idVec3 up = trace.endpos + playerAxis[2] * 64;
|
|
const idVec4 &color = ( trace.fraction >= 1.0f ) ? colorGreen : colorRed;
|
|
gameRenderWorld->DebugArrow( color, trace.endpos, up + playerAxis[1] * 32, 1, 0, true );
|
|
gameRenderWorld->DebugArrow( color, trace.endpos, up - playerAxis[1] * 32, 1, 0, true );
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::ShowObstaclePVS
|
|
============
|
|
*/
|
|
void idAASLocal::ShowObstaclePVS( const int areaNum ) const {
|
|
const byte *pvs = GetObstaclePVS( areaNum );
|
|
|
|
for ( int i = 0; i < file->GetNumAreas(); i++ ) {
|
|
if ( !IsInObstaclePVS( pvs, i ) ) {
|
|
continue;
|
|
}
|
|
DrawArea( i );
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::ShowManualReachabilities
|
|
============
|
|
*/
|
|
void idAASLocal::ShowManualReachabilities() const {
|
|
for ( int i = 0; i < file->GetNumReachabilityNames(); i++ ) {
|
|
const aasName_t &name = file->GetReachabilityName( i );
|
|
int index = file->FindReachabilityByName( name.name );
|
|
if ( index >= 0 ) {
|
|
DrawReachability( &file->GetReachability( index ), name.name );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
idAASLocal::ShowAASObstacles
|
|
==================
|
|
*/
|
|
void idAASLocal::ShowAASObstacles() const {
|
|
|
|
idPlayer *player = gameLocal.GetLocalPlayer();
|
|
|
|
if ( !player ) {
|
|
return;
|
|
}
|
|
|
|
idMat3 viewAxis = player->GetViewAxis();
|
|
|
|
for( int i = MAX_CLIENTS; i < MAX_GENTITIES; i++ ) {
|
|
|
|
idEntity* ent = gameLocal.entities[ i ];
|
|
|
|
if ( ent == NULL ) {
|
|
continue;
|
|
}
|
|
|
|
idAASObstacleEntity* obstacle = ent->Cast< idAASObstacleEntity >();
|
|
|
|
if ( obstacle == NULL ) {
|
|
continue;
|
|
}
|
|
|
|
idVec4 colorType = colorGreen;
|
|
|
|
if ( !obstacle->IsEnabled() ) {
|
|
colorType = colorRed;
|
|
}
|
|
|
|
idVec3 org = obstacle->GetPhysics()->GetAbsBounds().GetCenter();
|
|
|
|
gameRenderWorld->DebugBounds( colorType, obstacle->GetPhysics()->GetAbsBounds() );
|
|
gameRenderWorld->DrawText( va( "Team: %i", obstacle->GetTeam() ), org, 0.20f, colorWhite, viewAxis );
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::ShowAASBadAreas
|
|
============
|
|
*/
|
|
void idAASLocal::ShowAASBadAreas( int mode ) const {
|
|
|
|
if ( file == NULL ) {
|
|
return;
|
|
}
|
|
|
|
float height = file->GetSettings().boundingBox[1][2] - file->GetSettings().boundingBox[0][2];
|
|
|
|
for ( int i = 0; i < file->GetNumAreas(); i++ ) {
|
|
const aasArea_t &area = file->GetArea( i );
|
|
|
|
idVec3 mid;
|
|
mid.Zero();
|
|
|
|
for ( int j = 0; j < area.numEdges; j++ ) {
|
|
int edgeNum = file->GetEdgeIndex( area.firstEdge + j );
|
|
const aasEdge_t &edge = file->GetEdge( abs( edgeNum ) );
|
|
mid += file->GetVertex( edge.vertexNum[ INT32_SIGNBITSET( edgeNum ) ] );
|
|
}
|
|
|
|
mid /= area.numEdges;
|
|
|
|
bool bad = false;
|
|
|
|
if ( mode == 1 || mode == 3 ) {
|
|
int j;
|
|
for ( j = 0; j < 4; j++ ) {
|
|
int areaNum = file->PointAreaNum( mid + file->GetSettings().invGravityDir * ( j * height ) );
|
|
if ( areaNum == i ) {
|
|
break;
|
|
}
|
|
}
|
|
if ( j >= 4 ) {
|
|
bad = true;
|
|
}
|
|
}
|
|
|
|
if ( mode == 2 || mode == 3 ) {
|
|
#if 1
|
|
if ( ( area.flags & AAS_AREA_NOPUSH ) != 0 ) {
|
|
bad = true;
|
|
}
|
|
#else
|
|
idVec3 pushed = mid;
|
|
if ( file->PushPointIntoArea( i, pushed ) ) {
|
|
bad = true;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if ( bad ) {
|
|
DrawArea( i );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::GetAreaNumAndLocation
|
|
============
|
|
*/
|
|
bool idAASLocal::GetAreaNumAndLocation( idCVar &cvar, const idVec3 &origin, int &areaNum, idVec3 &location ) const {
|
|
|
|
areaNum = 0;
|
|
location.Zero();
|
|
|
|
if ( cvar.GetString()[0] == '\0' ) {
|
|
return false;
|
|
}
|
|
|
|
if ( idStr::Icmp( cvar.GetString(), "memory" ) == 0 ) {
|
|
cvar.SetString( aas_locationMemory.GetString() );
|
|
}
|
|
|
|
if ( idStr::Icmp( cvar.GetString(), "current" ) == 0 ) {
|
|
cvar.SetString( origin.ToString() );
|
|
}
|
|
|
|
idLexer src( LEXFL_NOERRORS|LEXFL_NOWARNINGS );
|
|
src.LoadMemory( cvar.GetString(), idStr::Length( cvar.GetString() ), "areaNum" );
|
|
|
|
bool error = false;
|
|
location.x = src.ParseFloat( &error );
|
|
location.y = src.ParseFloat( &error );
|
|
location.z = src.ParseFloat( &error );
|
|
|
|
if ( !error ) {
|
|
areaNum = PointReachableAreaNum( location, DefaultSearchBounds(), AAS_AREA_REACHABLE_WALK, TravelFlagInvalidForTeam() );
|
|
PushPointIntoArea( areaNum, location );
|
|
return true;
|
|
}
|
|
|
|
src.Reset();
|
|
|
|
areaNum = src.ParseInt();
|
|
|
|
if ( ( areaNum > 0 ) && ( areaNum < file->GetNumAreas() ) ) {
|
|
location = AreaCenter( areaNum );
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASLocal::Test
|
|
============
|
|
*/
|
|
void idAASLocal::Test( const idVec3 &origin ) {
|
|
int areaNum;
|
|
idVec3 location;
|
|
|
|
if ( file == NULL ) {
|
|
return;
|
|
}
|
|
|
|
if ( GetAreaNumAndLocation( aas_locationMemory, origin, areaNum, location ) ) {
|
|
}
|
|
if ( GetAreaNumAndLocation( aas_showPath, origin, areaNum, location ) ) {
|
|
ShowWalkPath( origin, areaNum, location );
|
|
}
|
|
if ( GetAreaNumAndLocation( aas_showHopPath, origin, areaNum, location ) ) {
|
|
ShowHopPath( origin, areaNum, location );
|
|
}
|
|
if ( GetAreaNumAndLocation( aas_pullPlayer, origin, areaNum, location ) ) {
|
|
int startAreaNum, travelTime;
|
|
ShowWalkPath( origin, areaNum, location );
|
|
PullPlayer( origin, areaNum, location, startAreaNum, travelTime );
|
|
} else {
|
|
UnFreezePlayer();
|
|
}
|
|
if ( aas_randomPullPlayer.GetInteger() != 0 ) {
|
|
RandomPullPlayer( origin, aas_randomPullPlayer.GetInteger() );
|
|
}
|
|
if ( aas_showAreas.GetInteger() != 0 ) {
|
|
ShowArea( origin, aas_showAreas.GetInteger() );
|
|
}
|
|
|
|
if ( aas_showAreaNumber.GetInteger() != 0 ) {
|
|
DrawArea( aas_showAreaNumber.GetInteger() );
|
|
}
|
|
|
|
if ( ( aas_showNearestCoverArea.GetInteger() > 0 ) && ( aas_showNearestCoverArea.GetInteger() < file->GetNumAreas() ) ) {
|
|
ShowNearestCoverArea( origin, aas_showNearestCoverArea.GetInteger() );
|
|
}
|
|
if ( aas_showNearestInsideArea.GetBool() ) {
|
|
ShowNearestInsideArea( origin );
|
|
}
|
|
if ( aas_showWallEdges.GetInteger() != 0 ) {
|
|
ShowWallEdges( origin, aas_showWallEdges.GetInteger(), aas_showWallEdgeNums.GetBool() );
|
|
}
|
|
if ( aas_showPushIntoArea.GetBool() ) {
|
|
ShowPushIntoArea( origin );
|
|
}
|
|
if ( aas_showFloorTrace.GetBool() ) {
|
|
ShowFloorTrace( origin );
|
|
}
|
|
if ( GetAreaNumAndLocation( aas_showObstaclePVS, origin, areaNum, location ) ) {
|
|
ShowObstaclePVS( areaNum );
|
|
}
|
|
if ( aas_showManualReachabilities.GetBool() ) {
|
|
ShowManualReachabilities();
|
|
}
|
|
if ( aas_showFuncObstacles.GetBool() ) {
|
|
ShowAASObstacles();
|
|
}
|
|
if ( aas_showBadAreas.GetInteger() != 0 ) {
|
|
ShowAASBadAreas( aas_showBadAreas.GetInteger() );
|
|
}
|
|
}
|