383 lines
9.7 KiB
C++
383 lines
9.7 KiB
C++
// Copyright (C) 2007 Id Software, Inc.
|
|
//
|
|
|
|
#include "../precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "../Game_local.h"
|
|
#include "BotThreadData.h"
|
|
#include "BotAI_Main.h"
|
|
|
|
idBotActions::idBotActions() {
|
|
actionBBox.Clear();
|
|
posture = WALK;
|
|
actionState = ACTION_STATE_NORMAL;
|
|
radius = 70.0f;
|
|
groupID = 0;
|
|
routeID = -1; //dont use a route by default.
|
|
activeForever = false;
|
|
baseHumanObj = humanObj = ACTION_NULL;
|
|
baseStroggObj = stroggObj = ACTION_NULL;
|
|
active = false;
|
|
actionTimeInSeconds = 0;
|
|
areaNum = 0;
|
|
areaNumVehicle = 0;
|
|
disguiseSafe = true;
|
|
noHack = false;
|
|
name = "";
|
|
targetAction = "";
|
|
requiresVehicleType = false;
|
|
origin.Zero();
|
|
blindFire = false;
|
|
priority = true;
|
|
teamOwner = NOTEAM;
|
|
weapType = NULL_WEAP;
|
|
baseActionType = BASE_ACTION;
|
|
actionVehicleFlags = -1; //mal: by default, no vehicle used.
|
|
spawnControllerEntityNum = -1;
|
|
actionActivateTime = 0;
|
|
memset( actionTargets, 0, sizeof( actionTargets ) );
|
|
|
|
//mal_TODO: add more variables here to clear as create them.
|
|
}
|
|
|
|
/*
|
|
================
|
|
idBotActions::EntityOriginIsInsideActionBBox
|
|
================
|
|
*/
|
|
bool idBotActions::EntityOriginIsInsideActionBBox( const idVec3& entOrg ) {
|
|
idBox actionBounds = actionBBox;
|
|
actionBounds.ExpandSelf( ACTION_BBOX_EXPAND_BIG ); //mal: be a bit forgiving when doing this bounds check.
|
|
|
|
if ( actionBounds.ContainsPoint( entOrg ) ) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idBotActions::EntityIsInsideActionBBox
|
|
|
|
Is the entity inside the bbox of the action?
|
|
================
|
|
*/
|
|
bool idBotActions::EntityIsInsideActionBBox( int entNum, const dangerTypes_t entityType, bool expandBox ) const {
|
|
int i, j;
|
|
idBox entBounds;
|
|
idBox actionBounds = actionBBox;
|
|
|
|
if ( expandBox ) {
|
|
actionBounds.ExpandSelf( ACTION_BBOX_EXPAND_BIG ); //mal: be a bit forgiving when doing this bounds check.
|
|
}
|
|
|
|
if ( entityType == PLANTED_CHARGE ) {
|
|
for( i = 0; i < MAX_CLIENT_CHARGES; i++ ) {
|
|
const plantedChargeInfo_t& charge = botThreadData.GetBotWorldState()->chargeInfo[ i ];
|
|
|
|
if ( charge.entNum != entNum ) {
|
|
continue;
|
|
}
|
|
|
|
if ( actionBounds.ContainsPoint( charge.origin ) ) {
|
|
return true;
|
|
}
|
|
}
|
|
} else if ( entityType == PLANTED_LANDMINE ) {
|
|
for( i = 0; i < MAX_CLIENTS; i++ ) {
|
|
for( j = 0; j < MAX_MINES; j++ ) {
|
|
|
|
if ( botThreadData.GetGameWorldState()->clientInfo[ i ].weapInfo.landMines[ j ].entNum != entNum ) {
|
|
continue;
|
|
}
|
|
|
|
if ( actionBounds.ContainsPoint( botThreadData.GetBotWorldState()->clientInfo[ i ].weapInfo.landMines[ j ].origin ) ) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
} else if ( entityType == INGAME_PLAYER ) {
|
|
entBounds = idBox( botThreadData.GetBotWorldState()->clientInfo[ entNum ].localBounds, botThreadData.GetBotWorldState()->clientInfo[ entNum ].origin, botThreadData.GetBotWorldState()->clientInfo[ entNum ].bodyAxis );
|
|
entBounds.ExpandSelf( 16.0f ); //mal: make it a bit flexible.
|
|
if ( actionBounds.IntersectsBox( entBounds ) ) {
|
|
return true;
|
|
}
|
|
} //mal_TODO: keep adding more entity types here!
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idBotActions::ArmedChargesInsideActionBBox
|
|
|
|
Is there any armed charges currently inside this action's bbox?
|
|
================
|
|
*/
|
|
bool idBotActions::ArmedChargesInsideActionBBox( int ignoreEnt ) const {
|
|
|
|
idBox actionBox = actionBBox;
|
|
actionBox.ExpandSelf( ACTION_BBOX_EXPAND_BIG );
|
|
|
|
for( int i = 0; i < MAX_CLIENT_CHARGES; i++ ) {
|
|
const plantedChargeInfo_t& bombInfo = botThreadData.GetBotWorldState()->chargeInfo[ i ];
|
|
|
|
if ( bombInfo.entNum == 0 ) {
|
|
continue;
|
|
}
|
|
|
|
if ( bombInfo.entNum == ignoreEnt ) {
|
|
continue;
|
|
}
|
|
|
|
if ( bombInfo.state != BOMB_ARMED ) {
|
|
continue;
|
|
}
|
|
|
|
idBox bombBox = idBox( bombInfo.bbox );
|
|
|
|
if ( actionBox.IntersectsBox( bombBox ) ) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idBotActions::ArmedMinesInsideActionBBox
|
|
|
|
Is there any armed mines currently inside this action's bbox?
|
|
================
|
|
*/
|
|
bool idBotActions::ArmedMinesInsideActionBBox() const {
|
|
for( int j = 0; j < MAX_CLIENTS; j++ ) {
|
|
for( int i = 0; i < MAX_MINES; i++ ) {
|
|
if ( botThreadData.GetBotWorldState()->clientInfo[ j ].weapInfo.landMines[ i ].entNum == 0 ) {
|
|
continue;
|
|
}
|
|
|
|
plantedMineInfo_t mine = botThreadData.GetBotWorldState()->clientInfo[ j ].weapInfo.landMines[ i ];
|
|
|
|
if ( mine.state != BOMB_ARMED ) {
|
|
continue;
|
|
}
|
|
|
|
idBox mineBox = idBox( mine.bbox );
|
|
idBox actionBox = actionBBox;
|
|
|
|
actionBox.ExpandSelf( ACTION_BBOX_EXPAND_BIG );
|
|
|
|
if ( botThreadData.AllowDebugData() ) {
|
|
gameRenderWorld->DebugBox( colorGreen, mineBox, 1024 );
|
|
gameRenderWorld->DebugBox( colorRed, actionBox, 1024 );
|
|
}
|
|
|
|
if ( actionBox.IntersectsBox( mineBox ) ) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idBotActions::FindBBoxCenterLinePoint
|
|
|
|
Find a point to look in the actions bbox, staying in the bounds of the box.
|
|
================
|
|
*/
|
|
void idBotActions::FindBBoxCenterLinePoint( idVec3& point ) const {
|
|
int index = Max3Index( actionBBox.GetExtents()[ 0 ], actionBBox.GetExtents()[ 1 ], actionBBox.GetExtents()[ 2 ] );
|
|
float extents = actionBBox.GetExtents()[ index ];
|
|
|
|
if ( extents > 64.0f ) {
|
|
extents -= 16.0f;
|
|
} else {
|
|
extents *= 0.80f;
|
|
}
|
|
|
|
idVec3 dir = actionBBox.GetAxis()[ index ] * extents;
|
|
idVec3 start = actionBBox.GetCenter() + dir;
|
|
idVec3 end = actionBBox.GetCenter() - dir;
|
|
|
|
ProjectPointOntoLine( start, end, point );
|
|
|
|
/*
|
|
idBounds bounds = actionBBox;
|
|
idVec3 v = bounds[1] - bounds[0];
|
|
int i = MaxIndex( v[0], v[1] );
|
|
idVec3 start = bounds.GetCenter();
|
|
idVec3 end = bounds.GetCenter();
|
|
start[i] = bounds[0][i] + 16.0f; //mal: move in from the start/end points just a bit, incase the bbox is right at the edge of a wall.
|
|
end[i] = bounds[1][i] - 16.0f;
|
|
*/
|
|
|
|
//mal_DEBUG
|
|
if ( botThreadData.AllowDebugData() ) {
|
|
gameRenderWorld->DebugLine( colorGreen, start, end, 1024 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idBotActions::ProjectPointOntoLine
|
|
================
|
|
*/
|
|
void idBotActions::ProjectPointOntoLine( const idVec3 &start, const idVec3 &end, idVec3 &point ) const {
|
|
idVec3 v1 = end - start;
|
|
float l = v1.Normalize();
|
|
idVec3 v2 = point - start;
|
|
float d = ( v1 * v2 );
|
|
if ( d < 0.0f ) {
|
|
point = start;
|
|
} else if ( d > l ) {
|
|
point = end;
|
|
}
|
|
|
|
point = start + d * v1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idBotActions::FindRandomPointInBBox
|
|
|
|
Find a random point in a action's bbox, along the center axis, for the bot to move toward.
|
|
================
|
|
*/
|
|
void idBotActions::FindRandomPointInBBox( idVec3 &point, int ignoreClientNum, const playerTeamTypes_t team ) const {
|
|
int index = Max3Index( actionBBox.GetExtents()[ 0 ], actionBBox.GetExtents()[ 1 ], actionBBox.GetExtents()[ 2 ] );
|
|
float extents = actionBBox.GetExtents()[ index ];
|
|
|
|
if ( extents > 64.0f ) {
|
|
extents -= 16.0f;
|
|
} else {
|
|
extents *= 0.80f;
|
|
}
|
|
|
|
idVec3 dir = actionBBox.GetAxis()[ index ] * extents;
|
|
idVec3 start = actionBBox.GetCenter() + dir;
|
|
idVec3 end = actionBBox.GetCenter() - dir;
|
|
|
|
point = actionBBox.GetCenter();
|
|
|
|
point = start + botThreadData.random.RandomFloat() * ( end - start );
|
|
|
|
int j = 0;
|
|
int i;
|
|
|
|
while( j < 10 ) {
|
|
for( i = 0; i < MAX_CLIENTS; i++ ) {
|
|
|
|
if ( i == ignoreClientNum ) { //mal: dont scan the calling client.
|
|
continue;
|
|
}
|
|
|
|
const clientInfo_t& player = botThreadData.GetBotWorldState()->clientInfo[ i ];
|
|
|
|
if ( player.inGame == false || player.team == NOTEAM || player.team != team || player.health <= 0 ) {
|
|
continue;
|
|
}
|
|
|
|
idVec3 vec = player.origin - point;
|
|
vec.z = 0.0f;
|
|
|
|
float z = vec.z;
|
|
|
|
if ( idMath::Fabs( z ) > 128.0f ) {
|
|
continue;
|
|
}
|
|
|
|
if ( vec.LengthSqr() > Square( SAFE_PLAYER_BODY_WIDTH ) ) {
|
|
continue;
|
|
}
|
|
|
|
j++;
|
|
|
|
point = actionBBox.GetCenter();
|
|
point = start + botThreadData.random.RandomFloat() * ( end - start );
|
|
break;
|
|
}
|
|
|
|
if ( i >= 32 ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( botThreadData.AllowDebugData() ) {
|
|
end = point;
|
|
end[ 2 ] += 64;
|
|
gameRenderWorld->DebugLine( colorRed, point, end, 1024 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idBotActions::CheckTeamMemberIsInsideAction
|
|
================
|
|
*/
|
|
bool idBotActions::CheckTeamMemberIsInsideAction( int ignoreClientNum, const playerTeamTypes_t team, const playerClassTypes_t classType, bool needsBombCharge ) const {
|
|
bool hasMate = false;
|
|
|
|
for( int i = 0; i < MAX_CLIENTS; i++ ) {
|
|
|
|
if ( i == ignoreClientNum ) {
|
|
continue;
|
|
}
|
|
|
|
const clientInfo_t& player = botThreadData.GetBotWorldState()->clientInfo[ i ];
|
|
|
|
if ( player.inGame == false || player.team == NOTEAM || player.team != team || player.health <= 0 || player.classType != classType ) {
|
|
continue;
|
|
}
|
|
|
|
idBox playerBox = idBox( player.localBounds, player.origin, player.bodyAxis );
|
|
playerBox.ExpandSelf( 16.0f );
|
|
|
|
if ( !actionBBox.IntersectsBox( playerBox ) ) {
|
|
continue;
|
|
}
|
|
|
|
if ( needsBombCharge ) {
|
|
if ( player.bombChargeUsed > 0 ) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
hasMate = true;
|
|
break;
|
|
}
|
|
|
|
return hasMate;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idBotActions::GetActionVehicleFlags
|
|
|
|
only allow the Icarus to be used if the action is inside a valid vehicle AAS area.
|
|
================
|
|
*/
|
|
int idBotActions::GetActionVehicleFlags( const playerTeamTypes_t team ) const {
|
|
if ( actionVehicleFlags == 0 && areaNumVehicle == 0 && team == STROGG ) {
|
|
return ALL_VEHICLES_BUT_ICARUS;
|
|
}
|
|
|
|
if ( actionVehicleFlags == 1 && areaNumVehicle == 0 && team == STROGG ) {
|
|
return NO_VEHICLE;
|
|
}
|
|
|
|
if ( actionVehicleFlags != -1 && areaNumVehicle == 0 && ( actionVehicleFlags & PERSONAL ) && team == STROGG ) {
|
|
int newFlags = actionVehicleFlags;
|
|
newFlags &= ~PERSONAL;
|
|
return newFlags;
|
|
}
|
|
|
|
return actionVehicleFlags;
|
|
}
|
|
|