etqw-sdk/source/game/botai/BotThreadData.cpp

6418 lines
202 KiB
C++

// Copyright (C) 2007 Id Software, Inc.
//
#include "../precompiled.h"
#pragma hdrstop
#if defined( _DEBUG ) && !defined( ID_REDIRECT_NEWDELETE )
#define new DEBUG_NEW
#endif
#include "../Game_local.h"
#include "../misc/DefenceTurret.h"
#include "../decls/GameDeclIdentifiers.h"
#include "../../game/ContentMask.h"
#include "../misc/PlayerBody.h"
#include "../script/Script_Helper.h"
#include "../script/Script_ScriptObject.h"
#include "../rules/GameRules.h"
#include "../vehicles/Transport.h"
#include "../vehicles/VehicleControl.h"
#include "BotThread.h"
#include "BotThreadData.h"
#include "Bot.h"
#include "../Misc.h"
#include "../../sdnet/SDNetSession.h"
idBotThreadData botThreadData;
/*
================
idBotThreadData::idBotThreadData
================
*/
idBotThreadData::idBotThreadData() {
personalVehicleClipModel = NULL;
genericVehicleClipModel = NULL;
goliathVehicleClipModel = NULL;
}
/*
================
idBotThreadData::~idBotThreadData
================
*/
idBotThreadData::~idBotThreadData() {
}
/*
==================
idBotThreadData::NumAAS
==================
*/
int idBotThreadData::NumAAS() const {
return aasList.Num();
}
/*
==================
idBotThreadData::GetAAS
==================
*/
idAAS *idBotThreadData::GetAAS( int num ) const {
if ( ( num >= 0 ) && ( num < aasList.Num() ) ) {
if ( aasList[ num ] && aasList[ num ]->GetSettings() ) {
return aasList[ num ];
}
}
return NULL;
}
/*
==================
idBotThreadData::GetAAS
==================
*/
idAAS *idBotThreadData::GetAAS( const char *name ) const {
int i;
for ( i = 0; i < aasNames.Num(); i++ ) {
if ( aasNames[ i ] == name ) {
if ( !aasList[ i ]->GetSettings() ) {
return NULL;
} else {
return aasList[ i ];
}
}
}
return NULL;
}
/*
==================
idBotThreadData::ShowAASStats
==================
*/
void idBotThreadData::ShowAASStats() const {
if ( IsThreadingEnabled() ) {
return;
}
for ( int i = 0; i < aasList.Num(); i++ ) {
aasList[i]->Stats();
}
}
/*
==================
idBotThreadData::InitClientInfo
==================
*/
void idBotThreadData::InitClientInfo( int clientNum, bool resetAll, bool leaving ) {
clientInfo_t &client = GetGameWorldState()->clientInfo[ clientNum ];
botAIOutput_t& botUcmd = GetBotOutputState()->botOutput[ clientNum ];
if ( resetAll ) {
client.classType = NOCLASS;
client.team = NOTEAM;
}
memset( &botUcmd, 0, sizeof( botUcmd ) );
botUcmd.debugInfo.botGoalType = NULL_GOAL_TYPE;
botUcmd.ackKillForClient = -1;
botUcmd.ackRepairForClient = -1;
botUcmd.desiredChat = NULL_CHAT;
client.inGame = true;
client.isActor = false;
client.briefingTime = 0;
client.escortSpawnID = -1;
client.escortRequestTime = 0;
client.lastRoadKillTime = 0;
client.gpmgOrigin = vec3_zero;
client.usingMountedGPMG = false;
client.mountedGPMGEntNum = -1;
client.hasTeleporterInWorld = false;
client.hasRepairDroneInWorld = false;
client.resetState = 0;
client.lastShieldDroppedTime = 0;
client.backPedalTime = 0;
client.wantsVehicle = false;
client.areaNum = 0;
client.areaNumVehicle = 0;
client.spectatorCounter = 0;
client.needsParachute = false;
client.covertWarningTime = 0;
client.myHero = -1;
client.mySavior = -1;
client.missionEntNum = -1;
client.supplyCrateRequestTime = 0;
memset( client.lastChatTime, 0, sizeof ( client.lastChatTime ) );
client.lastThanksTime = 0;
client.chatDelay = 0;
client.lastClassChangeTime = 0;
client.lastWeapChangedTime = 0;
memset( &client.weapInfo, 0, sizeof( client.weapInfo ) );
memset( client.packs, 0, sizeof( client.packs ) );
memset( client.kills, -1, sizeof( client.kills ) );
memset( client.forceShields, 0, sizeof( client.forceShields ) );
memset( &client.abilities, 0, sizeof( client.abilities ) );
client.killCounter = 0;
client.isCamper = false;
client.favoriteKill = -1;
client.killsSinceSpawn = 0;
client.disguisedClient = -1;
client.lastAttackClient = -1;
client.lastAttacker = -1;
client.lastAttackClientTime = -1;
client.lastAttackerTime = -1;
client.hasCrosshairHint = false;
client.isInRadar = false;
client.classChargeUsed = 0;
client.deviceChargeUsed = 0;
client.deployChargeUsed = 0;
client.bombChargeUsed = 0;
client.fireSupportChargedUsed = 0;
client.inPlayZone = true;
client.killTargetNum = -1;
client.killTargetSpawnID = -1;
client.killTargetUpdateTime = 0;
client.killTargetNeedsChat = false;
client.pickupRequestTime = 0;
client.pickupTargetSpawnID = -1;
client.commandRequestTime = 0;
client.commandRequestChatSent = false;
client.repairTargetNum = -1;
client.repairTargetSpawnID = -1;
client.repairTargetUpdateTime = 0;
client.repairTargetNeedsChat = false;
client.disguisedClient = -1;
client.enemiesInArea = 0;
client.friendsInArea = 0;
client.hasGroundContact = false;
client.hasJumped = false;
client.inEnemyTerritory = false;
client.spawnHostTargetSpawnID = -1;
client.inLimbo = false;
client.invulnerableEndTime = 0;
client.inWater = false;
client.isDisguised = false;
client.isMovingForward = false;
client.isNoTarget = false;
client.posture = IS_STANDING;
client.oldOrigin = vec3_zero;
client.revived = false;
client.spawnTime = 0;
client.targetLockEntNum = -1;
client.targetLockTime = 0;
client.deployDelayTime = 0;
client.proxyInfo.entNum = -1;
client.proxyInfo.time = 0;
client.proxyInfo.weapon = NULL_VEHICLE_WEAPON;
client.proxyInfo.weaponIsReady = false;
client.proxyInfo.weaponAxis = mat3_zero;
client.proxyInfo.weaponOrigin = vec3_zero;
client.proxyInfo.hasTurretWeapon = false;
client.proxyInfo.clientChangedSeats = false;
client.proxyInfo.boostCharge = 0.0f;
client.supplyCrate.entNum = 0;
client.supplyCrate.checkedAreaNum = false;
client.supplyCrate.areaNum = 0;
client.supplyCrate.origin = vec3_zero;
client.supplyCrate.team = NOTEAM;
client.supplyCrate.xySpeed = 0.0f;
client.xySpeed = 0.0f;
client.touchingItemTime = -1;
client.justSpawned = true;
client.weapInfo.primaryWeaponNeedsUpdate = true;
client.crouchCounter = 0;
if ( !leaving ) {
client.scriptHandler.chargeTimer = gameLocal.AllocTargetTimer( "energy_timer" );
client.scriptHandler.bombTimer = gameLocal.AllocTargetTimer( "energy_charge" );
client.scriptHandler.fireSupportTimer = gameLocal.AllocTargetTimer( "energy_firesupport" );
client.scriptHandler.deviceTimer = gameLocal.AllocTargetTimer( "energy_device" );
client.scriptHandler.deployTimer = gameLocal.AllocTargetTimer( "energy_deployment" );
client.scriptHandler.supplyTimer = gameLocal.AllocTargetTimer( "energy_supply" );
} else {
client.inGame = false;
client.isBot = false;
}
}
/*
==================
idBotThreadData::ResetClientsInfo
==================
*/
void idBotThreadData::ResetClientsInfo() {
int i;
idPlayer* player;
for( i = 0; i < MAX_CLIENTS; i++ ) {
player = gameLocal.GetClient( i );
if ( player == NULL ) {
continue;
}
InitClientInfo( player->entityNumber, false, false );
GetGameWorldState()->clientInfo[ player->entityNumber ].resetState = MAJOR_RESET_EVENT;
}
}
/*
==================
idBotThreadData::DrawActionPaths
==================
*/
void idBotThreadData::DrawActionPaths() {
if ( bot_testPathToBotAction.IsModified() ) {
aas_showPath.SetInteger( 0 );
bot_testPathToBotAction.ClearModified();
}
if ( bot_testPathToBotAction.GetInteger() == -1 ) {
return;
}
idPlayer *player = gameLocal.GetLocalPlayer();
if ( !player ) {
return;
}
idMat3 viewAxis = player->GetViewAxis();
if ( player->GetGameTeam() == NULL ) {
gameLocal.Warning("Must join a team before using this cvar!");
bot_testPathToBotAction.SetInteger( -1 );
return;
}
const playerTeamTypes_t playerTeam = player->GetGameTeam()->GetBotTeam();
int actionNumber = bot_testPathToBotAction.GetInteger();
if ( actionNumber <= ACTION_NULL || actionNumber > botActions.Num() ) {
gameLocal.Warning("Invalid action set by bot_testPathToBotAction!");
bot_testPathToBotAction.SetInteger( -1 );
return;
}
if ( botActions[ actionNumber ] == NULL ) {
gameLocal.Warning("Invalid action set by bot_testPathToBotAction!");
bot_testPathToBotAction.SetInteger( -1 );
return;
}
if ( botActions[ actionNumber ]->baseActionType != BASE_ACTION ) {
gameLocal.Warning("Invalid action set by bot_testPathToBotAction!");
bot_testPathToBotAction.SetInteger( -1 );
return;
}
idAAS *aas = GetAAS( aas_test.GetInteger() );
if ( aas == NULL ) {
gameLocal.Warning("No valid aas exists for aas_type %i!", aas_test.GetInteger() );
bot_testPathToBotAction.SetInteger( -1 );
return;
}
int excludeTravelFlags = ( playerTeam == GDF ) ? ( TFL_INVALID | TFL_INVALID_GDF ) : ( TFL_INVALID | TFL_INVALID_STROGG );
idVec3 actionOrg = botActions[ actionNumber ]->GetActionOrigin();
int areaNum = aas->PointReachableAreaNum( actionOrg, aas->GetSettings()->boundingBox, AAS_AREA_REACHABLE_WALK, excludeTravelFlags );
if ( areaNum == 0 ) {
gameLocal.Warning("Can't find a valid area for action %i!", bot_testPathToBotAction.GetInteger() );
bot_testPathToBotAction.SetInteger( -1 );
return;
}
aas_showPath.SetInteger( areaNum );
DrawActionNumber( actionNumber, viewAxis, true );
}
/*
==================
idBotThreadData::DrawDefuseHints
==================
*/
void idBotThreadData::DrawDefuseHints() {
for( int i = 0; i < botLocationRemap.Num(); i++ ) {
gameRenderWorld->DebugBox( colorRed, botLocationRemap[ i ]->bbox );
gameRenderWorld->DebugArrow( colorYellow, botLocationRemap[ i ]->bbox.GetCenter(), botLocationRemap[ i ]->target, 4 );
gameRenderWorld->DebugLine( colorGreen, botLocationRemap[ i ]->target, botLocationRemap[ i ]->target + idVec3( 0, 0, 64 ) );
}
}
/*
==================
idBotThreadData::DrawActions
==================
*/
void idBotThreadData::DrawActions() {
int i, j;
idMat3 viewAxis;
idPlayer *player;
player = gameLocal.GetLocalPlayer();
idVec3 end;
if ( !player ) {
return;
}
viewAxis = player->GetViewAxis();
if ( bot_drawIcarusActions.GetBool() ) {
for( i = 0; i < botActions.Num(); i++ ) {
if ( botActions[ i ] == NULL ) {
continue;
}
if ( botActions[ i ]->baseActionType != BASE_ACTION ) {
continue;
}
if ( botActions[ i ]->actionVehicleFlags != 0 && !( botActions[ i ]->actionVehicleFlags & PERSONAL ) || botActions[ i ]->actionVehicleFlags == -1 ) {
continue;
}
if ( botActions[ i ]->areaNumVehicle == 0 ) {
continue;
}
if ( botActions[ i ]->GetStroggObj() == ACTION_NULL ) {
continue;
}
end = botActions[ i ]->origin - player->GetPhysics()->GetOrigin();
if ( end.LengthSqr() > Square( bot_drawActionDist.GetFloat() ) ) {
continue;
}
DrawActionNumber( i, viewAxis, false );
}
} else if ( bot_drawBadIcarusActions.GetBool() ) {
for( i = 0; i < botActions.Num(); i++ ) {
if ( botActions[ i ] == NULL ) {
continue;
}
if ( botActions[ i ]->baseActionType != BASE_ACTION ) {
continue;
}
if ( botActions[ i ]->actionVehicleFlags != 0 && !( botActions[ i ]->actionVehicleFlags & PERSONAL ) || botActions[ i ]->actionVehicleFlags == -1 ) {
continue;
}
if ( botActions[ i ]->areaNumVehicle > 0 ) {
continue;
}
if ( botActions[ i ]->GetStroggObj() == ACTION_NULL ) {
continue;
}
end = botActions[ i ]->origin - player->GetPhysics()->GetOrigin();
if ( end.LengthSqr() > Square( bot_drawActionDist.GetFloat() ) ) {
continue;
}
DrawActionNumber( i, viewAxis, false );
gameRenderWorld->DebugCircle( colorRed, botActions[ i ]->origin, idVec3( 0, 0, 1 ), botActions[ i ]->radius, 8 );
}
} else if ( bot_drawActions.GetBool() ) {
for( i = 0; i < botActions.Num(); i++ ) {
if ( botActions[ i ] == NULL ) {
continue;
}
if ( botActions[ i ]->baseActionType != BASE_ACTION ) {
continue;
}
if ( bot_drawActionNumber.GetInteger() != -1 ) {
if ( i != bot_drawActionNumber.GetInteger() ) {
continue;
}
}
if ( bot_drawActionRoutesOnly.GetInteger() != -1 ) {
if ( botActions[ i ]->routeID != bot_drawActionRoutesOnly.GetInteger() ) {
continue;
}
}
if ( bot_drawActionWithClasses.GetBool() ) {
if ( botActions[ i ]->GetValidClasses() == 0 ) {
continue;
}
}
int actionFilter = bot_drawActiveActionsOnly.GetInteger();
botActionGoals_t actionType = ( botActionGoals_t ) bot_drawActionTypeOnly.GetInteger();
if ( actionType != ACTION_NULL ) {
if ( botActions[ i ]->GetHumanObj() != actionType && botActions[ i ]->GetStroggObj() != actionType ) {
continue;
}
}
if ( actionFilter > 0 ) {
if ( !botActions[ i ]->ActionIsActive() ) {
continue;
}
if ( actionFilter == 2 ) {
if ( botActions[ i ]->GetHumanObj() == ACTION_NULL ) {
continue;
}
} else if ( actionFilter == 3 ) {
if ( botActions[ i ]->GetStroggObj() == ACTION_NULL ) {
continue;
}
}
}
if ( bot_drawActionGroupNum.GetInteger() > -1 ) {
if ( botActions[ i ]->GetActionGroup() != bot_drawActionGroupNum.GetInteger() ) {
continue;
}
}
if ( bot_drawActionVehicleType.GetInteger() > -1 ) {
if ( botActions[ i ]->actionVehicleFlags == -1 || !( bot_drawActionVehicleType.GetInteger() & botActions[ i ]->actionVehicleFlags ) && bot_drawActionVehicleType.GetInteger() != 0 ) {
continue;
}
}
end = botActions[ i ]->origin - player->GetPhysics()->GetOrigin();
if ( end.LengthSqr() > Square( bot_drawActionDist.GetFloat() ) ) {
continue;
}
DrawActionNumber( i, viewAxis, true );
}
}
//mal: now draw all of the different route nodes out there..
if ( bot_drawRoutes.GetBool() ) {
for( i = 0; i < botRoutes.Num(); i++ ) {
if ( botRoutes[ i ] == NULL ) {
continue;
}
if ( bot_drawActiveRoutesOnly.GetBool() ) {
if ( !botRoutes[ i ]->active ) {
continue;
}
}
if ( bot_drawRouteGroupOnly.GetInteger() != -1 ) {
if ( botRoutes[ i ]->GetRouteGroup() != bot_drawRouteGroupOnly.GetInteger() ) {
continue;
}
}
end = botRoutes[ i ]->origin;
end[ 2 ] += 64.0f;
gameRenderWorld->DebugLine( ( botRoutes[ i ]->isHeadNode == true ) ? colorPink : colorDkRed, botRoutes[ i ]->origin, end, 16 );
end[ 2 ] += 8.0f;
gameRenderWorld->DrawText( va( "Route %i RouteID %i", i, botRoutes[ i ]->groupID ), end, 0.20f, colorWhite, viewAxis );
end[ 2 ] += 8.0f;
gameRenderWorld->DrawText( va( "Team %i Active %i", botRoutes[ i ]->team, botRoutes[ i ]->active ), end, 0.20f, colorWhite, viewAxis );
gameRenderWorld->DebugCircle( ( botRoutes[ i ]->isHeadNode == true ) ? colorYellow : colorGreen, botRoutes[ i ]->origin, idVec3( 0, 0, 1 ), botRoutes[ i ]->radius, 8 );
for( j = 0; j < botRoutes[ i ]->routeLinks.Num(); j++ ) {
gameRenderWorld->DebugArrow( colorMdGrey, botRoutes[ i ]->origin, botRoutes[ i ]->routeLinks[ j ]->origin, 16 );
}
}
}
}
/*
==================
idBotThreadData::DrawDynamicObstacles
==================
*/
void idBotThreadData::DrawDynamicObstacles() {
idMat3 viewAxis;
idPlayer *player;
player = gameLocal.GetLocalPlayer();
if ( !player ) {
return;
}
viewAxis = player->GetViewAxis();
for( int i = 0; i < botObstacles.Num(); i++ ) {
idVec3 vec = botObstacles[ i ]->bbox.GetCenter() - player->GetPhysics()->GetOrigin();
if ( vec.LengthSqr() > Square( 8192.f ) ) {
continue;
}
idVec4 colorType = ( botObstacles[ i ]->areaNum[ AAS_PLAYER ] == 0 || botObstacles[ i ]->areaNum[ AAS_VEHICLE ] == 0 ) ? colorRed : colorGreen;
gameRenderWorld->DebugBox( colorType, botObstacles[ i ]->bbox );
float radius = botObstacles[ i ]->bbox.GetExtents().ToVec2().Length();
idVec3 center = botObstacles[ i ]->bbox.GetCenter();
gameRenderWorld->DebugCircle( colorYellow, center, idVec3( 0, 0, 1 ), radius, 24 );
idVec3 top = center;
top.z += botObstacles[ i ]->bbox.GetExtents().Length() + 64.0f;
gameRenderWorld->DrawText( va( "Origin: %.1f %.1f %.1f", center.x, center.y, center.z ), top, 0.70f, colorWhite, viewAxis );
top.z += 32.0f;
gameRenderWorld->DrawText( va( "Obstacle: %i", i ), top, 0.70f, colorWhite, viewAxis );
top.z += 32.0f;
gameRenderWorld->DrawText( va( "Player AAS: %i Vehicle AAS: %i", botObstacles[ i ]->areaNum[ AAS_PLAYER ], botObstacles[ i ]->areaNum[ AAS_VEHICLE ] ), top, 0.70f, colorWhite, viewAxis );
}
}
/*
==================
idBotThreadData::DrawNumbers
Used in conjunction with the bot debug hud - lets me see client numbers so I can debug the bots AI easily.
==================
*/
void idBotThreadData::DrawNumbers() {
int i;
idMat3 viewAxis;
idPlayer *player;
player = gameLocal.GetLocalPlayer();
idVec3 end;
if ( !player ) {
return;
}
viewAxis = player->GetViewAxis();
for( i = 0; i < MAX_CLIENTS; i++ ) {
if ( GetGameWorldState()->clientInfo[ i ].inGame == false ) {
continue;
}
if ( i == player->entityNumber ) {
continue;
}
if ( GetGameWorldState()->clientInfo[ i ].team == NOTEAM ) {
continue;
}
idPlayer* drawPlayer = gameLocal.GetClient( i );
if ( drawPlayer == NULL ) {
continue;
}
end = drawPlayer->GetPhysics()->GetOrigin();
end[ 2 ] += 82;
gameRenderWorld->DrawText( va( "%i", i ), end, 1.5f, colorWhite, viewAxis );
}
}
/*
==================
idBotThreadData::InitAAS
==================
*/
void idBotThreadData::InitAAS( const idMapFile *mapFile ) {
assert( !botThread->IsActive() );
aasList.DeleteContents( true );
aasNames.Clear();
// get the aas settings definitions
sdDeclWrapperTemplate< sdDeclStringMap > declStringMapType;
declStringMapType.Init( declStringMapIdentifier );
const sdDeclStringMap* stringMap = declStringMapType.LocalFind( "aas_types", false );
const idKeyValue *kv = stringMap->GetDict().MatchPrefix( "type" );
while( kv != NULL ) {
idAAS *aas = idAAS::Alloc();
aasList.Append( aas );
aasNames.Append( kv->GetValue() );
kv = stringMap->GetDict().MatchPrefix( "type", kv );
}
// load navigation system for all the different player/vehicle sizes
for( int i = 0; i < aasNames.Num(); i++ ) {
aasList[ i ]->Init( idStr( mapFile->GetName() ).SetFileExtension( aasNames[ i ] ).c_str(), mapFile->GetGeometryCRC() );
}
//mal: now, lets do a check to make sure that at least the player AAS was loaded - if not, we're in trouble!
if ( aasList.Num() == 0 ) {
common->Warning( "No valid AAS file found for this map! The bots won't be able to play without one!\nConsult the manual for information on how to create an AAS file." );
}
pendingAreaChanges.SetNum( 0, false );
pendingReachChanges.SetNum( 0, false );
idVec3 mins = idVec3( 16.f, 16.f, 32.f );
idVec3 maxs = idVec3( -16.f, -16.f, 0.f );
personalVehicleClipModel = new idClipModel( idTraceModel( idBounds( mins, maxs ) ), false );
mins = idVec3( 32.f, 32.f, 32.f );
maxs = idVec3( -32.f, -32.f, 0.f );
genericVehicleClipModel = new idClipModel( idTraceModel( idBounds( mins, maxs ) ), false );
mins = idVec3( 64.f, 32.f, 64.f );
maxs = idVec3( -64.f, -32.f, 0.f );
goliathVehicleClipModel = new idClipModel( idTraceModel( idBounds( mins, maxs ) ), false );
}
/*
==================
idBotThreadData::UpdateState
==================
*/
void idBotThreadData::UpdateState() {
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
idPlayer *player = gameLocal.GetClient( i );
if ( player == NULL ) {
continue;
}
clientInfo_t &client = GetGameWorldState()->clientInfo[ i ];
if ( client.inGame == false || client.team == NOTEAM ) {
continue;
}
if ( player->GetTeleportEntity() == NULL && ( ( player->GetPhysics()->HasGroundContacts() || player->GetPhysics()->InWater() ) || player->GetProxyEntity() != NULL || player->GetNoClip() ) ) {
idVec3 aasOrigin = player->GetPhysics()->GetOrigin();
int areaNum = Nav_GetAreaNumAndOrigin( AAS_PLAYER, client.origin, aasOrigin );
if ( areaNum > 0 ) { //mal: be sure to cache only the last valid area
client.areaNum = areaNum;
client.aasOrigin = aasOrigin;
}
sdTransport* transport = player->GetProxyEntity()->Cast< sdTransport >();
if ( transport == NULL ) {
aasOrigin = player->GetPhysics()->GetOrigin();
} else {
aasOrigin = transport->GetPhysics()->GetOrigin() + ( transport->GetPhysics()->GetAxis() * transport->GetPhysics()->GetBounds( -1 ).GetCenter() );
}
areaNum = Nav_GetAreaNumAndOrigin( AAS_VEHICLE, aasOrigin, aasOrigin );
if ( areaNum > 0 ) { //mal: be sure to cache only the last valid area
client.areaNumVehicle = areaNum;
client.aasVehicleOrigin = aasOrigin;
}
}
client.oldOrigin = player->GetPhysics()->GetOrigin();
client.weapInfo.sideArmHasAmmo = player->GetInventory().CheckWeaponSlotHasAmmo( PISTOL_SLOT );
client.weapInfo.primaryWeapHasAmmo = player->GetInventory().CheckWeaponSlotHasAmmo( GUN_SLOT );
client.weapInfo.hasNadeAmmo = player->GetInventory().CheckWeaponSlotHasAmmo( GRENADE_SLOT );
if ( client.weapInfo.primaryWeaponNeedsUpdate ) {
player->GetInventory().UpdatePrimaryWeapon();
client.weapInfo.primaryWeaponNeedsUpdate = false;
}
if ( client.isBot ) {
const botAIOutput_t& ucmd = botThreadData.GetGameOutputState()->botOutput[ i ];
if ( ucmd.ackKillForClient > -1 && ucmd.ackKillForClient < MAX_CLIENTS ) {
botThreadData.GetGameWorldState()->clientInfo[ ucmd.ackKillForClient ].killTargetNeedsChat = false;
}
if ( ucmd.ackRepairForClient > -1 && ucmd.ackRepairForClient < MAX_CLIENTS ) {
botThreadData.GetGameWorldState()->clientInfo[ ucmd.ackRepairForClient ].repairTargetNeedsChat = false;
}
}
}
if ( GetGameWorldState()->botGoalInfo.isTrainingMap ) {
if ( !MapHasAnActor() ) {
FindBotToBeActor();
}
TrainingThink();
}
CheckCurrentChatRequests();
DynamicEntity_Think(); //mal: check all dynamic entities out there.
//mal: next, run some data collection for the bot thread: get info about dead bodies around the bot...
sdInstanceCollector< sdPlayerBody > bodies( false );
for ( int i = 0; i < MAX_PLAYERBODIES; i++ ) {
playerBodiesInfo_t &body = GetGameWorldState()->playerBodies[ i ];
if ( i >= bodies.Num() ) {
body.isValid = false;
body.areaNum = 0;
continue;
}
sdPlayerBody *stiff = bodies[ i ];
if ( stiff ) {
idPlayer *player = stiff->GetClient();
if ( !player ) { //mal: they prolly got kicked/disconnected - we'll ignore those bodies.
body.isValid = false;
continue;
}
if ( stiff->GetPhysics()->InWater() > 0.0f ) { //currently, there is no under water swimming, so ignore bodies in water. If this changes, update
body.isValid = false;
continue;
}
body.isValid = true;
body.bodyTeam = bodies[ i ]->GetGameTeam()->GetBotTeam();
body.bodyOwnerClientNum = player->entityNumber;
body.bodyOrigin = bodies[ i ]->GetPhysics()->GetOrigin();
body.bodyNum = stiff->entityNumber;
body.spawnID = gameLocal.GetSpawnId( stiff );
if ( body.areaNum == 0 ) {
body.areaNum = Nav_GetAreaNum( AAS_PLAYER, body.bodyOrigin );
}
if ( body.areaNum == 0 ) {
body.isValid = false;
}
if ( body.bodyTeam == GDF ) {
sdScriptHelper h1;
body.isSpawnHostAble = stiff->CallBooleanNonBlockingScriptEvent( stiff->isSpawnHostableFunc, h1 );
sdScriptHelper h2;
body.isSpawnHost = stiff->CallBooleanNonBlockingScriptEvent( stiff->isSpawnHostFunc, h2 );
} else {
body.isSpawnHostAble = false; //mal: can't spawnhost strogg bodies!
body.isSpawnHost = false;
}
sdScriptHelper h3;
body.uniformStolen = stiff->CallBooleanNonBlockingScriptEvent( stiff->hasNoUniformFunc, h3 );
}
}
//mal: update some info for the bot's thread.
gameLocalInfo_t &gameLocalInfo = GetGameWorldState()->gameLocalInfo;
gameLocalInfo.time = gameLocal.time;
gameLocalInfo.gameTimeInMinutes = ( int ) ( ( gameLocal.rules->GetGameTime() / 1000 ) / 60 );
gameLocalInfo.inEndGame = gameLocal.rules->IsEndGame();
if ( gameLocalInfo.inEndGame ) {
sdTeamInfo* winningTeam = gameLocal.rules->GetWinningTeam();
if ( winningTeam != NULL ) {
if ( winningTeam->GetIndex() == 0 ) {
gameLocalInfo.winningTeam = GDF;
} else {
gameLocalInfo.winningTeam = STROGG;
}
}
}
gameLocalInfo.gameIsBotMatch = ( networkSystem->IsDedicated() || networkSystem->IsLANServer() ) ? false : true;
gameLocalInfo.inWarmup = gameLocal.rules->IsWarmup();
gameLocalInfo.botsUseTKRevive = bot_useTKRevive.GetBool();
gameLocalInfo.numClients = gameLocal.numClients;
gameLocalInfo.heroMode = !bot_doObjectives.GetBool();
gameLocalInfo.botsCanStrafeJump = bot_useStrafeJump.GetBool();
gameLocalInfo.botsSillyWarmup = bot_sillyWarmup.GetBool();
gameLocalInfo.botsIgnoreGoals = bot_ignoreGoals.GetBool();
gameLocalInfo.botsUseSpawnHosts = bot_useSpawnHosts.GetBool();
gameLocalInfo.botsUseUniforms = bot_useUniforms.GetBool();
gameLocalInfo.botSkill = bot_skill.GetInteger();
if ( AllowDebugData() ) {
gameLocalInfo.botFollowPlayer = bot_followMe.GetInteger();
gameLocalInfo.botIgnoreEnemies = bot_ignoreEnemies.GetInteger();
} else {
gameLocalInfo.botFollowPlayer = 0;
gameLocalInfo.botIgnoreEnemies = 0;
}
if ( gameLocalInfo.botSkill == BOT_SKILL_DEMO && !gameLocalInfo.gameIsBotMatch ) { //mal_HACK: another 11th hour fix. ugh.
gameLocalInfo.botSkill = 2;
}
gameLocalInfo.botAimSkill = bot_aimSkill.GetInteger();
gameLocalInfo.friendlyFireOn = si_teamDamage.GetBool();
gameLocalInfo.botsPaused = bot_pause.GetBool() || gameLocal.IsPaused();
gameLocalInfo.botKnifeOnly = bot_knifeOnly.GetBool();
gameLocalInfo.debugBotWeapons = bot_debugWeapons.GetBool();
gameLocalInfo.debugBots = bot_debug.GetBool();
gameLocalInfo.debugObstacleAvoidance = bot_debugObstacleAvoidance.GetBool();
gameLocalInfo.botsUseVehicles = bot_useVehicles.GetBool();
gameLocalInfo.botsUseAirVehicles = bot_useAirVehicles.GetBool();
gameLocalInfo.botsStayInVehicles = bot_stayInVehicles.GetBool();
gameLocalInfo.botsUseDeployables = bot_useDeployables.GetBool();
gameLocalInfo.botsUseMines = bot_useMines.GetBool();
gameLocalInfo.debugPersonalVehicles = bot_debugPersonalVehicles.GetBool();
gameLocalInfo.debugAltRoutes = bot_useAltRoutes.GetBool();
gameLocalInfo.botsCanSuicide = bot_useSuicideWhenStuck.GetBool();
gameLocalInfo.botsCanDecayObstacles = bot_allowObstacleDecay.GetBool();
gameLocalInfo.botsSleep = false;
gameLocalInfo.botPauseInVehicleTime = bot_pauseInVehicleTime.GetInteger();
gameLocalInfo.botsDoObjsInTrainingMode = bot_doObjsInTrainingMode.GetBool();
gameLocalInfo.botTrainingModeObjDelayTime = ( bot_doObjsDelayTimeInMins.GetInteger() * 60 );
if ( bot_sleepWhenServerEmpty.GetBool() ) {
if ( !ServerHasHumans() ) {
gameLocalInfo.botsSleep = true;
}
}
#ifdef BOT_MOVE_LOOKUP
// FeaRog: let the bots rebuild the lookup tables for movement if they need to
// (this function won't actually do anything unless the cvars have changed)
idBot::BuildMoveLookups( pm_runspeedforward.GetFloat(), pm_runspeedstrafe.GetFloat(), pm_runspeedback.GetFloat(),
pm_sprintspeedforward.GetFloat(), pm_sprintspeedstrafe.GetFloat() );
#endif
//mal: update the current "humans on server" status for the "Hero" game mode.
bool hasGDFHumans = gameLocalInfo.teamGDFHasHuman;
bool hasSTROGGHumans = gameLocalInfo.teamStroggHasHuman;
gameLocalInfo.teamStroggHasHuman = TeamHasHumans( STROGG );
gameLocalInfo.teamGDFHasHuman = TeamHasHumans( GDF );
//mal: if the current human situation changed from last frame, update the bot AI. In hero mode, this will dictate whether they do objs or not.
if ( hasGDFHumans != gameLocalInfo.teamGDFHasHuman ) {
ResetBotAI( GDF );
}
if ( hasSTROGGHumans != gameLocalInfo.teamStroggHasHuman ) {
ResetBotAI( STROGG );
}
threadingEnabled = bot_threading.GetBool();
if ( IsThreadingEnabled() ) {
if ( com_timeServer.GetInteger() != 0 ) {
threadMinFrameDelay = 0;
threadMaxFrameDelay = 0;
} else {
threadMinFrameDelay = Min( bot_threadMinFrameDelay.GetInteger(), bot_threadMaxFrameDelay.GetInteger() );
threadMaxFrameDelay = bot_threadMaxFrameDelay.GetInteger();
}
assert( BOT_THREAD_BUFFER_SIZE >= threadMaxFrameDelay * 2 );
// let the bot thread know there's another game frame
botThread->SignalBotThread();
// wait if the bot thread is falling too far behind
while ( botThread->GetLastGameFrameNum() < gameLocal.GetFrameNum() - threadMaxFrameDelay && !botThread->IsWaiting() ) {
common->Warning( "Waiting on BotThread\n" );
botThread->WaitForBotThread();
}
} else {
// let the bots think from here
RunFrame();
}
botFPS = botThread->GetFrameRate();
}
/*
==================
idBotThreadData::RunFrame
==================
*/
void idBotThreadData::RunFrame() {
// apply any pending changes
ApplyPendingAreaChanges();
ApplyPendingReachabilityChanges();
// set latest game world state as current for the bots
SetCurrentBotWorldState();
for ( int i = 0; i < MAX_CLIENTS; i++ ) { //mal: run the bots think loop
if ( bots[i] != NULL ) {
bots[i]->Think();
}
}
// set latest bot output as current for the game
SetCurrentBotOutputState();
}
/*
================
idBotThreadData::AddActionToHash
================
*/
void idBotThreadData::AddActionToHash( const char *actionName, int actionNum ) {
if ( FindActionByName( actionName ) != -1 ) {
common->Error( "Multiple bot actions named '%s'", actionName );
}
actionHash.Add( actionHash.GenerateKey( actionName, true ), actionNum );
}
/*
================
idBotThreadData::FindActionByOrigin
================
*/
int idBotThreadData::FindActionByOrigin( const botActionGoals_t actionType, const idVec3& origin, bool activeOnly ) {
int i;
int actionNumber = -1;
idVec3 vec;
for( i = 0; i < botActions.Num(); i++ ) {
if ( activeOnly ) {
if ( !botActions[ i ]->ActionIsActive() ) {
continue;
}
}
if ( !botActions[ i ]->ActionIsValid() ) {
continue;
}
if ( actionType != ACTION_NULL ) {
if ( botActions[ i ]->GetHumanObj() != actionType && botActions[ i ]->GetStroggObj() != actionType ) {
continue;
}
}
if ( botActions[ i ]->actionBBox.IsCleared() ) { //mal: need a bbox type action to do this check!
continue;
}
vec = botActions[ i ]->GetActionOrigin() - origin;
if ( vec.LengthSqr() > Square( 1024.0f ) ) { //mal: too far away from our origin to matter
continue;
}
if ( !botActions[ i ]->actionBBox.ContainsPoint( origin ) ) {
continue;
}
actionNumber = i;
break;
}
return actionNumber;
}
/*
=============
idBotThreadData::FindActionName
Returns the action whose name matches the specified string.
=============
*/
int idBotThreadData::FindActionByName( const char *actionName ) {
int hash, i;
hash = actionHash.GenerateKey( actionName, true );
for ( i = actionHash.GetFirst( hash ); i != -1; i = actionHash.GetNext( i ) ) { //mal_FIXME: optimize this loop! Originally we tested for "isvalid" first.
if ( botActions[ i ]->name.Icmp( actionName ) == 0 ) {
return i;
}
}
return -1;
/*
int hash, i;
hash = actionHash.GenerateKey( actionName, true );
for ( i = actionHash.GetFirst( hash ); i != -1; i = actionHash.GetNext( i ) ) { //mal_FIXME: optimize this loop! Originally we tested for "isvalid" first.
if ( botActions[ i ]->name.Icmp( actionName ) == 0 ) {
if ( botActions[ i ]->ActionIsValid() ) {
return i;
}
return -1;
}
}
return -1;
*/
}
/*
================
idBotThreadData::Clear
Clears out all of the botThreadData data - called by idGameLocal::LoadMap
================
*/
void idBotThreadData::Clear( bool clearAll ) {
random.SetSeed( 0 );
aasList.DeleteContents( true );
aasNames.Clear();
botActions.DeleteContents( true );
botRoutes.DeleteContents( true );
botObstacles.DeleteContents( true );
botLocationRemap.DeleteContents( true );
botVehicleNodes.Clear();
actionHash.Clear();
routeHash.Clear();
if ( clearAll ) {
memset( bots, 0, sizeof( bots ) );
}
threadingEnabled = false;
actionsLoaded = false;
forwardSpawnsChecked = false;
memset( worldStateBuffer, 0, sizeof( worldStateBuffer ) );
memset( outputStateBuffer, 0, sizeof( outputStateBuffer ) );
currentWorldState = 0;
currentOutputState = 0;
gameWorldState = worldStateBuffer;
gameOutputState = outputStateBuffer;
botWorldState = worldStateBuffer;
botOutputState = outputStateBuffer;
botFPS = 0;
}
/*
================
idBotThreadData::Init
================
*/
void idBotThreadData::Init() {
clip = &gameLocal.clip;
GetGameWorldState()->botGoalInfo.carryableObjs.Memset( 0 );
GetGameWorldState()->smokeGrenades.Memset( 0 );
GetGameWorldState()->chargeInfo.Memset( 0 );
GetGameWorldState()->spawnHosts.Memset( 0 );
GetGameWorldState()->deployableInfo.Memset( 0 );
GetGameWorldState()->vehicleInfo.Memset( 0 );
GetGameWorldState()->playerBodies.Memset( 0 );
GetGameWorldState()->stroyBombs.Memset( 0 );
GetGameWorldState()->gameLocalInfo.maxVehicleHeight = gameLocal.flightCeilingUpper - 512.0f;
GetGameWorldState()->gameLocalInfo.crouchViewHeight = pm_crouchviewheight.GetFloat();
GetGameWorldState()->gameLocalInfo.proneViewHeight = pm_proneviewheight.GetFloat();
GetGameWorldState()->gameLocalInfo.normalViewHeight = pm_normalviewheight.GetFloat();
GetGameWorldState()->botGoalInfo.team_GDF_criticalClass = NOCLASS;
GetGameWorldState()->botGoalInfo.team_STROGG_criticalClass = NOCLASS;
GetGameWorldState()->botGoalInfo.attackingTeam = NOTEAM;
GetGameWorldState()->botGoalInfo.team_GDF_PrimaryAction = ACTION_NULL;
GetGameWorldState()->botGoalInfo.team_STROGG_PrimaryAction = ACTION_NULL;
GetGameWorldState()->botGoalInfo.team_GDF_SecondaryAction = ACTION_NULL;
GetGameWorldState()->botGoalInfo.team_STROGG_SecondaryAction = ACTION_NULL;
GetGameWorldState()->botGoalInfo.team_GDF_AttackDeployables = true;
GetGameWorldState()->botGoalInfo.team_STROGG_AttackDeployables = true;
GetGameWorldState()->botGoalInfo.botSightDist = ENEMY_SIGHT_DIST; //mal: nice default value
GetGameWorldState()->botGoalInfo.botGoal_MCP_VehicleNum = -1;
GetGameWorldState()->botGoalInfo.mapHasMCPGoal = false;
GetGameWorldState()->botGoalInfo.mapHasMCPGoalTime = 0;
GetGameWorldState()->botGoalInfo.gameIsOnFinalObjective = false;
GetGameWorldState()->botGoalInfo.isTrainingMap = false;
GetGameWorldState()->gameLocalInfo.winningTeam = NOTEAM;
GetGameWorldState()->botGoalInfo.teamNeededClassInfo[ GDF ].criticalClass = NOCLASS;
GetGameWorldState()->botGoalInfo.teamNeededClassInfo[ GDF ].numCriticalClass = 0;
GetGameWorldState()->botGoalInfo.teamNeededClassInfo[ GDF ].donatingClass = NOCLASS;
GetGameWorldState()->botGoalInfo.teamRetreatInfo[ GDF ].retreatTime = 0;
GetGameWorldState()->gameLocalInfo.teamUsesSpawnInfo[ GDF ].teamUsesSpawn = false;
GetGameWorldState()->gameLocalInfo.teamUsesSpawnInfo[ GDF ].percentage = 20;
GetGameWorldState()->gameLocalInfo.teamMineInfo[ GDF ].isPriority = true;
GetGameWorldState()->botGoalInfo.teamNeededClassInfo[ STROGG ].criticalClass = NOCLASS;
GetGameWorldState()->botGoalInfo.teamNeededClassInfo[ STROGG ].numCriticalClass = 0;
GetGameWorldState()->botGoalInfo.teamNeededClassInfo[ STROGG ].donatingClass = NOCLASS;
GetGameWorldState()->botGoalInfo.teamRetreatInfo[ STROGG ].retreatTime = 0;
GetGameWorldState()->gameLocalInfo.teamUsesSpawnInfo[ STROGG ].teamUsesSpawn = false;
GetGameWorldState()->gameLocalInfo.teamUsesSpawnInfo[ STROGG ].percentage = 20;
GetGameWorldState()->gameLocalInfo.teamMineInfo[ STROGG ].isPriority = true;
GetGameWorldState()->botGoalInfo.deliverActionNumber = FindDeliverActionNumber();
checkedLastMapStageCovertWeapons = false;
lastCmdDeclinedChatTime = 0;
lastWeapChangedTime = 0;
nextBotClassUpdateTime = 0;
nextDeployableUpdateTime = 0;
nextChatUpdateTime = 0;
packUpdateTime = 0;
teamsSwapedRecently = false;
ignoreActionNumber = ACTION_NULL;
actorMissionInfo.actionNumber = ACTION_NULL;
actorMissionInfo.hasBriefedPlayer = false;
actorMissionInfo.deployableStageIsActive = false;
actorMissionInfo.targetClientNum = -1;
actorMissionInfo.actorClientNum = -1;
actorMissionInfo.setup = false;
actorMissionInfo.chatPauseTime = 0;
actorMissionInfo.playerNeedsFinalBriefing = false;
actorMissionInfo.playerIsOnFinalMission = false;
actorMissionInfo.goalPauseTime = 0;
actorMissionInfo.setupBotNames = true;
actorMissionInfo.forwardSpawnIsAllowed = false;
actorMissionInfo.hasEnteredActorNode = false;
}
/*
================
idBotThreadData::DebugOutput
My dump heap for all things debug related.
================
*/
void idBotThreadData::DebugOutput() {
idPlayer* player = gameLocal.GetLocalPlayer();
if ( !player ) {
return;
}
//mal: debug the aas!
if ( !IsThreadingEnabled() ) {
idAAS *aas = GetAAS( aas_test.GetInteger() );
if ( aas != NULL ) {
aas->Test( player->GetPhysics()->GetOrigin() );
}
// test an obstacle avoidance query
if ( bot_testObstacleQuery.GetString()[0] != '\0' ) {
idObstacleAvoidance obstacleAvoidance;
obstacleAvoidance.TestQuery( bot_testObstacleQuery.GetString(), aas );
}
if ( bot_debugPersonalVehicles.GetBool() ) {
for( int i = 0; i < botActions.Num(); i++ ) {
botActions[ i ]->actionVehicleFlags = 1;
}
}
}
//mal: debug actions!
DrawActions();
if ( bot_drawObstacles.GetBool() ) {
DrawDynamicObstacles();
}
if ( bot_drawDefuseHints.GetBool() ) {
DrawDefuseHints();
}
if ( bot_drawRearSpawnLocations.GetBool() ) {
DrawRearSpawns();
}
botThreadData.botVehicleNodes.DrawNodes();
if ( bot_drawClientNumbers.GetBool() ) {
DrawNumbers();
}
DrawActionPaths();
// common->Printf("Is Leaning = %i\n", player->GetPlayerPhysics().IsLeaning() );
// common->Printf("Locked On = %i\n", GetGameWorldState()->clientInfo[ player->entityNumber ].enemyHasLockon );
// idVec3 vec = GetGameWorldState()->clientInfo[ 1 ].origin - GetGameWorldState()->clientInfo[ player->entityNumber ].origin;
// common->Printf("Dist = %f\n", vec.LengthFast() );
// common->Printf("Target = %i Target Locked = %i\n", GetGameWorldState()->clientInfo[ player->entityNumber ].targetLockEntNum, GetGameWorldState()->clientInfo[ player->entityNumber ].targetLocked );
// common->Printf("Is Scoped = %i\n", GetGameWorldState()->clientInfo[ player->entityNumber ].weapInfo.isScopeUp );
// common->Printf("Fire Support = %i\n", GetGameWorldState()->clientInfo[ player->entityNumber ].fireSupportChargedUsed );
// common->Printf("Needs Reload = %i Can Reload = %i\n", GetGameWorldState()->clientInfo[ player->entityNumber ].weapInfo.primaryWeapNeedsReload, GetGameWorldState()->clientInfo[ player->entityNumber ].weapInfo.hasAmmoForReload );
// common->Printf("Weapon = %i\n", GetGameWorldState()->clientInfo[ player->entityNumber ].weapInfo.weapon );
// common->Printf("Smoke State = %i\n", GetGameWorldState()->clientInfo[ player->entityNumber ].deviceChargeUsed );
// common->Printf("Bomb State = %i\n", GetGameWorldState()->clientInfo[ player->entityNumber ].weapInfo.bombState );
// common->Printf("Speed = %f\n", GetGameWorldState()->clientInfo[ player->entityNumber ].xySpeed );
// common->Printf("Weapon Ready = %i\n", GetGameWorldState()->clientInfo[ player->entityNumber ].proxyInfo.weaponIsReady );
// if ( player->GetProxyEntity() != NULL ) {
// float proxySpeed = player->GetProxyEntity()->GetPhysics()->GetLinearVelocity() * player->GetProxyEntity()->GetRenderEntity()->axis[ 0 ];
// common->Printf( "Forward Speed = %f\n", proxySpeed );
// float vehicleYaw = player->GetProxyEntity()->GetRenderEntity()->axis.ToAngles().yaw;
// float vehiclePitch = player->GetProxyEntity()->GetRenderEntity()->axis.ToAngles().pitch;
// float vehicleRoll = player->GetProxyEntity()->GetRenderEntity()->axis.ToAngles().roll;
// int cmdPitch = player->usercmd.angles[ PITCH ];
// common->Printf("Cmd Pitch = %i\n", cmdPitch );
// common->Printf("Pitch = %f, Yaw = %f, Roll = %f\n", vehiclePitch, vehicleYaw, vehicleRoll );
// }
// if ( player->GetProxyEntity() != NULL ) {
// idBox box = idBox( player->GetProxyEntity()->GetPhysics()->GetBounds(), player->GetProxyEntity()->GetPhysics()->GetOrigin(), player->GetProxyEntity()->GetPhysics()->GetAxis() );
// float expand = ( player->GetProxyEntity()->GetPhysics()->GetLinearVelocity() * player->GetProxyEntity()->GetRenderEntity()->axis[ 0 ] ) * 2.0f;
// box.ExpandSelf( idMath::Fabs( expand * 0.5f ), 0.0f, 0.0f );
// box.TranslateSelf( box.GetAxis()[0] * expand * 0.5f );
// gameRenderWorld->DebugBox( colorGreen, box, 16 );
// }
/*
idPlayer* botPlayer1 = gameLocal.GetClient( 1 );
idPlayer* botPlayer2 = gameLocal.GetClient( 2 );
if ( botPlayer1 != NULL && botPlayer2 != NULL ) {
bool humanCanSeeBot = botPlayer1->IsObscuredBySmoke( player );
bool botCanSeeHuman = player->IsObscuredBySmoke( botPlayer1 );
bool nextBotCanSeeHuman = player->IsObscuredBySmoke( botPlayer2 );
bool botCanSeeNextBot = botPlayer2->IsObscuredBySmoke( botPlayer1 );
bool nextBotCanSeeBot = botPlayer1->IsObscuredBySmoke( botPlayer2 );
bool humanCanSeeNextBot = botPlayer2->IsObscuredBySmoke( player );
gameLocal.Printf("I can't see human: %i, human can't see me: %i\n", botCanSeeHuman, humanCanSeeBot );
if ( humanCanSeeBot != botCanSeeHuman || botCanSeeNextBot != nextBotCanSeeBot || humanCanSeeNextBot != nextBotCanSeeHuman ) {
assert( false );
}
}
*/
// gameLocal.Printf("Charge: %f Altitude: %f\n", GetGameWorldState()->clientInfo[ player->entityNumber ].proxyInfo.boostCharge, GetGameWorldState()->clientInfo[ player->entityNumber ].altitude );
/*
trace_t tr;
idVec3 end = player->firstPersonViewOrigin;
end += ( 8092 * player->firstPersonViewAxis[ 0 ] );
gameLocal.TracePoint( tr, player->firstPersonViewOrigin, end, MASK_SHOT_BOUNDINGBOX | MASK_VEHICLESOLID | CONTENTS_RENDERMODEL );
gameLocal.Printf("Trace Entity: %i\n", tr.c.entityNum );
*/
/*
bool isClear = true;
for( int i = 0; i < botThreadData.botObstacles.Num(); i++ ) {
idBotObstacle *obstacle = botThreadData.botObstacles[ i ];
idVec3 vec = obstacle->bbox.GetCenter() - player->GetPhysics()->GetOrigin();
float distSqr = vec.LengthSqr();
distSqr -= obstacle->bbox.GetExtents().LengthSqr();
if ( distSqr > Square( 100.0f ) ) {
continue;
}
isClear = false;
break;
}
common->Printf("IsClear = %i\n", isClear );
*/
}
/*
================
idBotThreadData::LoadMap
================
*/
void idBotThreadData::LoadMap( const char* mapName, int randSeed ) {
random.SetSeed( randSeed );
//mal: here, setup the various timer globals so that the bots can see and use them.
//mal_NOTE: look at "etqw/base/defs/misc.def" to find all the diff key values.
const idDeclEntityDef* constants = gameLocal.declEntityDefType[ "globalConstants" ];
if ( constants == NULL ) {
gameLocal.Error( "Failed to find 'globalConstants' in /defs/misc.def" );
}
GetGameWorldState()->gameLocalInfo.energyTimerTime = constants->dict.GetFloat( "energy_timer_time" );
GetGameWorldState()->gameLocalInfo.bombTimerTime = constants->dict.GetFloat( "energy_charge_time" );
GetGameWorldState()->gameLocalInfo.fireSupportTimerTime = constants->dict.GetFloat( "energy_firesupport_time" );
GetGameWorldState()->gameLocalInfo.deviceTimerTime = constants->dict.GetFloat( "energy_device_time" );
GetGameWorldState()->gameLocalInfo.chargeExplodeTime = constants->dict.GetFloat( "charge_explode_time" );
GetGameWorldState()->gameLocalInfo.supplyTimerTime = constants->dict.GetFloat( "energy_supply_time" );
GetGameWorldState()->gameLocalInfo.deployTimerTime = constants->dict.GetFloat( "energy_deployment_time" );
if ( idStr::Icmp( mapName, "maps/area22.entities" ) == 0 ) {
GetGameWorldState()->gameLocalInfo.gameMap = AREA22;
} else if ( idStr::Icmp( mapName, "maps/ark.entities" ) == 0 ) {
GetGameWorldState()->gameLocalInfo.gameMap = ARK;
} else if ( idStr::Icmp( mapName, "maps/canyon.entities" ) == 0 ) {
GetGameWorldState()->gameLocalInfo.gameMap = CANYON;
} else if ( idStr::Icmp( mapName, "maps/island.entities" ) == 0 ) {
GetGameWorldState()->gameLocalInfo.gameMap = ISLAND;
} else if ( idStr::Icmp( mapName, "maps/outskirts.entities" ) == 0 ) {
GetGameWorldState()->gameLocalInfo.gameMap = OUTSKIRTS;
} else if ( idStr::Icmp( mapName, "maps/quarry.entities" ) == 0 ) {
GetGameWorldState()->gameLocalInfo.gameMap = QUARRY;
} else if ( idStr::Icmp( mapName, "maps/refinery.entities" ) == 0 ) {
GetGameWorldState()->gameLocalInfo.gameMap = REFINERY;
} else if ( idStr::Icmp( mapName, "maps/salvage.entities" ) == 0 ) {
GetGameWorldState()->gameLocalInfo.gameMap = SALVAGE;
} else if ( idStr::Icmp( mapName, "maps/sewer.entities" ) == 0 ) {
GetGameWorldState()->gameLocalInfo.gameMap = SEWER;
} else if ( idStr::Icmp( mapName, "maps/slipgate.entities" ) == 0 ) {
GetGameWorldState()->gameLocalInfo.gameMap = SLIPGATE;
} else if ( idStr::Icmp( mapName, "maps/valley.entities" ) == 0 ) {
GetGameWorldState()->gameLocalInfo.gameMap = VALLEY;
} else if ( idStr::Icmp( mapName, "maps/volcano.entities" ) == 0 ) {
GetGameWorldState()->gameLocalInfo.gameMap = VOLCANO;
} else {
GetGameWorldState()->gameLocalInfo.gameMap = UNKNOWN_MAP;
}
}
/*
================
idBotThreadData::SetCurrentGameWorldState
================
*/
void idBotThreadData::SetCurrentGameWorldState() {
// advance to the next world state
int nextWorldState = ( currentWorldState + 1 ) & (BOT_THREAD_BUFFER_SIZE-1);
memcpy( &worldStateBuffer[nextWorldState], &worldStateBuffer[currentWorldState], sizeof( worldStateBuffer[0] ) );
currentWorldState = nextWorldState;
gameWorldState = &worldStateBuffer[currentWorldState];
}
/*
================
idBotThreadData::SetCurrentGameOutputState
================
*/
void idBotThreadData::SetCurrentGameOutputState() {
// set pointer to the previous bot output state
int prevOutputState = ( currentOutputState + (BOT_THREAD_BUFFER_SIZE-1) ) & (BOT_THREAD_BUFFER_SIZE-1);
gameOutputState = &outputStateBuffer[prevOutputState];
}
/*
================
idBotThreadData::SetCurrentBotWorldState
================
*/
void idBotThreadData::SetCurrentBotWorldState() {
// set pointer to the previous world state
int prevWorldState = ( currentWorldState + (BOT_THREAD_BUFFER_SIZE-1) ) & (BOT_THREAD_BUFFER_SIZE-1);
botWorldState = &worldStateBuffer[prevWorldState];
}
/*
================
idBotThreadData::SetCurrentBotOutputState
================
*/
void idBotThreadData::SetCurrentBotOutputState() {
// advance to the next bot output state
int nextOutputState = ( currentOutputState + 1 ) & (BOT_THREAD_BUFFER_SIZE-1);
memcpy( &outputStateBuffer[nextOutputState], &outputStateBuffer[currentOutputState], sizeof( outputStateBuffer[0] ) );
currentOutputState = nextOutputState;
botOutputState = &outputStateBuffer[currentOutputState];
}
/*
=====================
idBotThreadData::RemoveBot
=====================
*/
void idBotThreadData::RemoveBot( int entityNumber ) {
botThread->Lock();
idBotAI *botAI = botThreadData.bots[ entityNumber ];
// set bot AI pointer to NULL
botThreadData.bots[ entityNumber ] = NULL;
if ( botAI != NULL ) {
botAI->nextRemoved = removedBotAIs;
removedBotAIs = botAI;
}
botThread->UnLock();
}
/*
================
idBotThreadData::DeleteBots
================
*/
void idBotThreadData::DeleteBots() {
botThread->Lock();
// remove any bots for real
for ( idBotAI *nextBotAI = NULL, *botAI = removedBotAIs; botAI != NULL; botAI = nextBotAI ) {
nextBotAI = botAI->nextRemoved;
delete botAI;
}
removedBotAIs = NULL;
botThread->UnLock();
}
/*
================
idBotThreadData::ApplyPendingAreaChanges
================
*/
void idBotThreadData::ApplyPendingAreaChanges() {
botThread->Lock();
for ( int i = 0; i < pendingAreaChanges.Num(); i++ ) {
for ( int j = 0; j < botThreadData.NumAAS(); j++ ) {
idAAS * aas = botThreadData.GetAAS( j );
if ( aas == NULL ) {
continue;
}
botAreaChange_t &change = pendingAreaChanges[i];
aas->ChangeAreaTravelFlags( change.bounds, change.areaFlags, change.travelFlags, change.set );
}
}
pendingAreaChanges.SetNum( 0, false );
botThread->UnLock();
}
/*
================
idBotThreadData::EnableArea
================
*/
void idBotThreadData::EnableArea( const idBounds &bounds, int areaFlags, int team, bool enable ) {
botThread->Lock();
botAreaChange_t change;
change.bounds = bounds;
change.areaFlags = areaFlags;
change.set = !enable;
switch( team ) {
case 0: change.travelFlags = TFL_INVALID_GDF; break;
case 1: change.travelFlags = TFL_INVALID_STROGG; break;
case 2: change.travelFlags = TFL_INVALID; break;
default: change.travelFlags = TFL_INVALID; break;
}
pendingAreaChanges.Append( change );
botThread->UnLock();
}
/*
================
idBotThreadData::ApplyPendingReachabilityChanges
================
*/
void idBotThreadData::ApplyPendingReachabilityChanges() {
botThread->Lock();
for ( int i = 0; i < pendingReachChanges.Num(); i++ ) {
for ( int j = 0; j < botThreadData.NumAAS(); j++ ) {
idAAS * aas = botThreadData.GetAAS( j );
if ( aas == NULL ) {
continue;
}
botReachChange_t &change = pendingReachChanges[i];
aas->ChangeReachabilityTravelFlags( change.name, change.travelFlags, change.set );
}
}
pendingReachChanges.SetNum( 0, false );
botThread->UnLock();
}
/*
================
idBotThreadData::DisableAASAreaInLocation
================
*/
void idBotThreadData::DisableAASAreaInLocation( int aasType, const idVec3& location ) {
if ( aasType < AAS_PLAYER || aasType > AAS_VEHICLE ) {
gameLocal.DWarning( "Invalid AAS type passed to \"disableAASAreaInLocation\"\nValid values are 0 = player aas, 1 = vehicle aas" );
return;
}
idAAS * aas = botThreadData.GetAAS( aasType );
if ( aas == NULL ) {
gameLocal.DWarning( "AAS type %i can't be found in call to \"disableAASAreaInLocation\"", aasType );
return;
}
int areaNum = aas->PointReachableAreaNum( location, aas->GetSettings()->boundingBox, AAS_AREA_REACHABLE_WALK, 0 );
if ( areaNum == 0 ) {
gameLocal.DWarning( "No aas area found at location X: %.0f Y: %.0f Z: %.0f in call to \"disableAASAreaInLocation\"", location.x, location.y, location.z );
return;
}
aas->SetAreaTravelFlags( areaNum, TFL_INVALID );
gameLocal.DPrintf( "Disabled areaNum %i at location X: %.0f Y: %.0f Z: %.0f\n", areaNum, location.x, location.y, location.z );
}
/*
================
idBotThreadData::EnableReachability
================
*/
void idBotThreadData::EnableReachability( const char *name, int team, bool enable ) {
botThread->Lock();
botReachChange_t change;
change.name = name;
change.set = !enable;
switch( team ) {
case 0: change.travelFlags = TFL_INVALID_GDF; break;
case 1: change.travelFlags = TFL_INVALID_STROGG; break;
case 2: change.travelFlags = TFL_INVALID; break;
default: change.travelFlags = TFL_INVALID; break;
}
pendingReachChanges.Append( change );
botThread->UnLock();
}
#define DYNAMIC_OBSTACLE_CULL_DISTANCE 4096
#define DYNAMIC_OBSTACLE_CONSIDER_DISTANCE 256
/*
================
idBotThreadData::BuildObstacleList
================
*/
bool idBotThreadData::BuildObstacleList( idObstacleAvoidance &obstacleAvoidance, const idVec3 &origin, const int areaNum, bool inVehicle ) const {
obstacleAvoidance.ClearObstacles();
for( int i = 0; i < botObstacles.Num(); i++ ) {
idBotObstacle *obstacle = botObstacles[ i ];
idVec3 vec = obstacle->bbox.GetCenter() - origin;
float radiusSqr = obstacle->bbox.GetExtents().LengthSqr();
float avoidObstacleRangeSqr;
// mal: if in a vehicle, avoid better!
if ( inVehicle ) {
avoidObstacleRangeSqr = radiusSqr + Square( 512.0f );
} else {
avoidObstacleRangeSqr = radiusSqr + Square( 256.0f );
}
if ( vec.LengthSqr() > avoidObstacleRangeSqr ) {
continue;
}
obstacleAvoidance.AddObstacle( obstacle->bbox, obstacle->num );
}
for( int i = 0; i < MAX_VEHICLES; i++ ) {
const proxyInfo_t &vehicleInfo = GetBotWorldState()->vehicleInfo[ i ];
if ( vehicleInfo.entNum == 0 ) {
continue;
}
idVec3 vec = vehicleInfo.origin - origin;
if ( vec.LengthSqr() > Square( DYNAMIC_OBSTACLE_CULL_DISTANCE ) ) {
continue;
}
//mal: enlarge the box based on the vehicles speed.
float expand = vehicleInfo.forwardSpeed * 2.0f;
idBox box( vehicleInfo.bbox, vehicleInfo.origin, vehicleInfo.axis );
box.ExpandSelf( idMath::Fabs( expand * 0.5f ), 0.0f, 0.0f );
box.TranslateSelf( box.GetAxis()[0] * expand * 0.5f );
if ( !box.Expand( DYNAMIC_OBSTACLE_CONSIDER_DISTANCE ).ContainsPoint( origin ) ) {
continue;
}
obstacleAvoidance.AddObstacle( box, vehicleInfo.entNum );
}
for( int i = 0; i < MAX_DEPLOYABLES; i++ ) {
const deployableInfo_t& deployable = GetBotWorldState()->deployableInfo[ i ];
if ( deployable.entNum == 0 ) {
continue;
}
if ( deployable.health <= 0 ) {
continue;
}
if ( !deployable.inPlace ) {
continue;
}
if ( deployable.ownerClientNum == -1 ) {
//continue;
}
idVec3 vec = deployable.origin - origin;
if ( vec.LengthSqr() > Square( 1024.0f ) ) {
continue;
}
idBox box( deployable.bbox, deployable.origin, deployable.axis );
obstacleAvoidance.AddObstacle( box, deployable.entNum );
}
for( int i = 0; i < MAX_CLIENTS; i++ ) {
if ( gameLocal.entities[i] == NULL ) {
continue;
}
// skip the local player
if ( gameLocal.GetLocalPlayer()->entityNumber == i ) {
continue;
}
const clientInfo_t& playerInfo = GetBotWorldState()->clientInfo[ i ];
if ( playerInfo.health <= 0 ) {
continue;
}
//mal: we'll test vehicles seperately.
if ( playerInfo.proxyInfo.entNum != CLIENT_HAS_NO_VEHICLE ) {
continue;
}
//mal: ignore players outside the valid AAS space
if ( playerInfo.areaNum == 0 ) {
continue;
}
idVec3 vec = playerInfo.origin - origin;
//mal: keep the range pretty tight.
if ( vec.LengthSqr() > Square( 1024.0f ) ) {
continue;
}
idBox box = idBox( playerInfo.localBounds, playerInfo.origin, playerInfo.bodyAxis );
obstacleAvoidance.AddObstacle( box, i );
}
return true;
}
/*
================
idBotThreadData::Printf
================
*/
void idBotThreadData::Printf( const char *fmt, ... ) {
if ( !bot_debug.GetBool() || IsThreadingEnabled() ) {
return;
}
va_list argptr;
va_start( argptr, fmt );
common->VPrintf( fmt, argptr );
va_end( argptr );
}
/*
================
idBotThreadData::Warning
================
*/
void idBotThreadData::Warning( const char *fmt, ... ) {
if ( !bot_debug.GetBool() || IsThreadingEnabled() ) {
return;
}
va_list argptr;
char msg[2048];
va_start( argptr, fmt );
idStr::vsnPrintf( msg, sizeof( msg ), fmt, argptr );
va_end( argptr );
msg[ sizeof( msg ) - 1 ] = 0;
common->Printf( S_COLOR_YELLOW "WARNING: " S_COLOR_RED "%s\n", msg );
}
/*
================
idBotThreadData::DynamicEntity_Think
Tracks things like health/ammo/supply packs, deployables, etc.
================
*/
void idBotThreadData::DynamicEntity_Think() {
int i, j, k;
int areaNum;
GetGameWorldState()->botGoalInfo.botGoal_MCP_VehicleNum = -1;
//mal: packs first....
for( j = 0; j < MAX_CLIENTS; j++ ) {
for( i = 0; i < MAX_ITEMS; i++ ) {
if ( GetGameWorldState()->clientInfo[ j ].packs[ i ].entNum <= 0 ) {
continue;
}
idEntity *pack = gameLocal.entities[ GetGameWorldState()->clientInfo[ j ].packs[ i ].entNum ];
if ( pack != NULL && gameLocal.GetSpawnId( pack ) == GetGameWorldState()->clientInfo[ j ].packs[ i ].spawnID ) {
GetGameWorldState()->clientInfo[ j ].packs[ i ].origin = pack->GetPhysics()->GetOrigin();
GetGameWorldState()->clientInfo[ j ].packs[ i ].xySpeed = pack->GetPhysics()->GetLinearVelocity().LengthFast();
GetGameWorldState()->clientInfo[ j ].packs[ i ].inWater = ( pack->GetPhysics()->InWater() > 0.0f ) ? true : false;
//mal: cache off the AAS areaNum data, so the bots understand if its reachable or not. Also, check if the pack is in the play zone or not.
if ( GetGameWorldState()->clientInfo[ j ].packs[ i ].xySpeed == 0.0f ) {
if ( GetGameWorldState()->clientInfo[ j ].packs[ i ].checkedAreaNum == false ) {
const sdPlayZone* playZone = NULL;
playZone = gameLocal.GetPlayZone( pack->GetPhysics()->GetOrigin(), sdPlayZone::PZF_PLAYZONE );
if ( playZone != NULL ) {
const sdDeployMaskInstance* deployMask = playZone->GetMask( gameLocal.GetPlayZoneMask() );
if ( deployMask == NULL || deployMask->IsValid( pack->GetPhysics()->GetAbsBounds() ) == DR_CLEAR ) {
GetGameWorldState()->clientInfo[ j ].packs[ i ].inPlayZone = true;
}
}
GetGameWorldState()->clientInfo[ j ].packs[ i ].areaNum = Nav_GetAreaNum( AAS_PLAYER, pack->GetPhysics()->GetOrigin() );
GetGameWorldState()->clientInfo[ j ].packs[ i ].checkedAreaNum = true;
}
if ( packUpdateTime < gameLocal.time ) {
bool allClear = true;
idVec3 packOrg = pack->GetPhysics()->GetOrigin();
idVec3 end = packOrg;
end.z += 82.0f;
idBounds packBounds = pack->GetPhysics()->GetAbsBounds();
packBounds.AddPoint( end );
idEntity* entityList[ 128 ];
int count = gameLocal.clip.EntitiesTouchingBounds( packBounds, MASK_VEHICLESOLID | CONTENTS_MONSTER | MASK_SHOT_RENDERMODEL | MASK_SHOT_BOUNDINGBOX, entityList, 128, true );
for ( int f = 0; f < count; f++ ) {
idEntity* other = entityList[ f ];
if ( other == pack ) {
continue;
}
allClear = false;
break;
}
GetGameWorldState()->clientInfo[ j ].packs[ i ].available = allClear;
}
} else {
GetGameWorldState()->clientInfo[ j ].packs[ i ].checkedAreaNum = false;
}
} else {
GetGameWorldState()->clientInfo[ j ].packs[ i ].entNum = 0;
GetGameWorldState()->clientInfo[ j ].packs[ i ].spawnID = -1;
GetGameWorldState()->clientInfo[ j ].packs[ i ].available = false;
GetGameWorldState()->clientInfo[ j ].packs[ i ].inPlayZone = false;
if ( bot_debug.GetBool() ) {
gameLocal.DWarning( "DynamicEntity_Think called a non-existent supply item!" );
}
}
}
}
if ( packUpdateTime < gameLocal.time ) {
packUpdateTime = gameLocal.time + 5000;
}
//mal: supply crates next....
for( j = 0; j < MAX_CLIENTS; j++ ) {
if ( GetGameWorldState()->clientInfo[ j ].supplyCrate.entNum <= 0 ) {
continue;
}
idEntity *crate = gameLocal.entities[ GetGameWorldState()->clientInfo[ j ].supplyCrate.entNum ];
if ( crate != NULL && gameLocal.GetSpawnId( crate ) == GetGameWorldState()->clientInfo[ j ].supplyCrate.spawnID ) {
GetGameWorldState()->clientInfo[ j ].supplyCrate.origin = crate->GetPhysics()->GetOrigin();
GetGameWorldState()->clientInfo[ j ].supplyCrate.xySpeed = crate->GetPhysics()->GetLinearVelocity().LengthFast();
GetGameWorldState()->clientInfo[ j ].supplyCrate.inWater = ( crate->GetPhysics()->InWater() > 0.0f ) ? true : false;
//mal: cache off the AAS areaNum data, so the bots understand if its reachable or not.
if ( GetGameWorldState()->clientInfo[ j ].supplyCrate.xySpeed == 0.0f ) {
if ( GetGameWorldState()->clientInfo[ j ].supplyCrate.checkedAreaNum == false ) {
GetGameWorldState()->clientInfo[ j ].supplyCrate.areaNum = Nav_GetAreaNum( AAS_PLAYER, crate->GetPhysics()->GetOrigin() );
GetGameWorldState()->clientInfo[ j ].supplyCrate.checkedAreaNum = true;
}
} else {
GetGameWorldState()->clientInfo[ j ].supplyCrate.checkedAreaNum = false;
}
} else {
GetGameWorldState()->clientInfo[ j ].supplyCrate.entNum = 0;
GetGameWorldState()->clientInfo[ j ].supplyCrate.spawnID = -1;
if ( bot_debug.GetBool() ) {
gameLocal.DWarning( "DynamicEntity_Think called a non-existent supply crate!" );
}
}
}
//mal: track airstrikes next...
for( j = 0; j < MAX_CLIENTS; j++ ) {
if ( GetGameWorldState()->clientInfo[ j ].weapInfo.airStrikeInfo.entNum <= 0 ) {
continue;
}
idEntity *airStrike = gameLocal.entities[ GetGameWorldState()->clientInfo[ j ].weapInfo.airStrikeInfo.entNum ];
if ( airStrike != NULL && gameLocal.GetSpawnId( airStrike ) == GetGameWorldState()->clientInfo[ j ].weapInfo.airStrikeInfo.spawnID ) {
GetGameWorldState()->clientInfo[ j ].weapInfo.airStrikeInfo.origin = airStrike->GetPhysics()->GetOrigin();
GetGameWorldState()->clientInfo[ j ].weapInfo.airStrikeInfo.xySpeed = airStrike->GetPhysics()->GetLinearVelocity().LengthFast();
} else {
GetGameWorldState()->clientInfo[ j ].weapInfo.airStrikeInfo.entNum = 0;
GetGameWorldState()->clientInfo[ j ].weapInfo.airStrikeInfo.timeTilStrike = 0;
GetGameWorldState()->clientInfo[ j ].weapInfo.airStrikeInfo.origin = vec3_zero;
GetGameWorldState()->clientInfo[ j ].weapInfo.airStrikeInfo.xySpeed = 0.0f;
GetGameWorldState()->clientInfo[ j ].weapInfo.airStrikeInfo.spawnID = -1;
if ( bot_debug.GetBool() ) {
gameLocal.DWarning( "DynamicEntity_Think called a non-existent airstrike!" );
}
}
}
//mal: track force shields next...
for( j = 0; j < MAX_CLIENTS; j++ ) {
for( i = 0; i < MAX_SHIELDS; i++ ) {
if ( GetGameWorldState()->clientInfo[ j ].forceShields[ i ].entNum <= 0 ) {
continue;
}
idEntity *forceShield = gameLocal.entities[ GetGameWorldState()->clientInfo[ j ].forceShields[ i ].entNum ];
if ( forceShield != NULL && gameLocal.GetSpawnId( forceShield ) == GetGameWorldState()->clientInfo[ j ].forceShields[ i ].spawnID ) {
GetGameWorldState()->clientInfo[ j ].forceShields[ i ].origin = forceShield->GetPhysics()->GetOrigin();
} else {
GetGameWorldState()->clientInfo[ j ].forceShields[ i ].entNum = 0;
GetGameWorldState()->clientInfo[ j ].forceShields[ i ].spawnID = -1;
GetGameWorldState()->clientInfo[ j ].forceShields[ i ].origin = vec3_zero;
if ( bot_debug.GetBool() ) {
gameLocal.DWarning( "DynamicEntity_Think called a non-existent force shield!" );
}
}
}
}
//mal: track stroybombs next...
for( j = 0; j < MAX_STROYBOMBS; j++ ) {
if ( GetGameWorldState()->stroyBombs[ j ].entNum <= 0 ) {
continue;
}
idEntity *stroyBomb = gameLocal.entities[ GetGameWorldState()->stroyBombs[ j ].entNum ];
if ( stroyBomb != NULL && gameLocal.GetSpawnId( stroyBomb ) == GetGameWorldState()->stroyBombs[ j ].spawnID ) {
GetGameWorldState()->stroyBombs[ j ].xySpeed = stroyBomb->GetPhysics()->GetLinearVelocity().LengthFast();
GetGameWorldState()->stroyBombs[ j ].origin = stroyBomb->GetPhysics()->GetOrigin();
} else {
GetGameWorldState()->stroyBombs[ j ].entNum = 0;
GetGameWorldState()->stroyBombs[ j ].spawnID = -1;
GetGameWorldState()->stroyBombs[ j ].origin = vec3_zero;
GetGameWorldState()->stroyBombs[ j ].xySpeed = 0.0f;
}
}
//mal: track hives/3rd eye cameras next...
for( j = 0; j < MAX_CLIENTS; j++ ) {
if ( GetGameWorldState()->clientInfo[ j ].weapInfo.covertToolInfo.entNum <= 0 ) {
continue;
}
idEntity *covertTool = gameLocal.entities[ GetGameWorldState()->clientInfo[ j ].weapInfo.covertToolInfo.entNum ];
if ( covertTool != NULL && gameLocal.GetSpawnId( covertTool ) == GetGameWorldState()->clientInfo[ j ].weapInfo.covertToolInfo.spawnID ) {
GetGameWorldState()->clientInfo[ j ].weapInfo.covertToolInfo.origin = covertTool->GetPhysics()->GetOrigin();
GetGameWorldState()->clientInfo[ j ].weapInfo.covertToolInfo.xySpeed = covertTool->GetPhysics()->GetLinearVelocity().LengthFast();
GetGameWorldState()->clientInfo[ j ].weapInfo.covertToolInfo.axis = covertTool->GetPhysics()->GetAxis();
} else {
GetGameWorldState()->clientInfo[ j ].weapInfo.covertToolInfo.entNum = 0;
GetGameWorldState()->clientInfo[ j ].weapInfo.covertToolInfo.origin = vec3_zero;
GetGameWorldState()->clientInfo[ j ].weapInfo.covertToolInfo.xySpeed = 0.0f;
GetGameWorldState()->clientInfo[ j ].weapInfo.covertToolInfo.clientIsUsing = false;
GetGameWorldState()->clientInfo[ j ].weapInfo.covertToolInfo.spawnID = -1;
GetGameWorldState()->clientInfo[ j ].weapInfo.covertToolInfo.axis = mat3_identity;
if ( bot_debug.GetBool() ) {
gameLocal.DWarning( "DynamicEntity_Think called a non-existent covert tool!" );
}
}
}
idAAS *aas = botThreadData.GetAAS( AAS_PLAYER );
//mal: update charges next.
for( j = 0; j < MAX_CLIENT_CHARGES; j++ ) {
if ( GetGameWorldState()->chargeInfo[ j ].entNum <= 0 ) {
continue;
}
idEntity *charge = gameLocal.entities[ GetGameWorldState()->chargeInfo[ j ].entNum ];
if ( charge != NULL && gameLocal.GetSpawnId( charge ) == GetGameWorldState()->chargeInfo[ j ].spawnID ) {
if ( AllowDebugData() && GetGameWorldState()->chargeInfo[ j ].checkedAreaNum ) {
idVec4 colorType = colorGreen;
if ( GetGameWorldState()->chargeInfo[ j ].areaNum == 0 ) {
colorType = colorRed;
}
gameRenderWorld->DebugBounds( colorType, charge->GetPhysics()->GetBounds(), charge->GetPhysics()->GetOrigin(), charge->GetPhysics()->GetAxis() );
}
if ( !GetGameWorldState()->chargeInfo[ j ].checkedAreaNum && aas != NULL ) {
playerTeamTypes_t playerTeam = GetGameWorldState()->chargeInfo[ j ].team;
int excludeTravelFlags = ( playerTeam == GDF ) ? ( TFL_INVALID | TFL_INVALID_GDF ) : ( TFL_INVALID | TFL_INVALID_STROGG );
idVec3 chargeOrigin = charge->GetPhysics()->GetOrigin();
botThreadData.RemapLocation( chargeOrigin );
int areaNum = aas->PointReachableAreaNum( chargeOrigin, aas->GetSettings()->boundingBox, AAS_AREA_REACHABLE_WALK, excludeTravelFlags );
bool canReach = true;
int actionAreaNum;
int travelFlags;
int travelTime;
idVec3 actionOrigin;
const aasReachability_t *reach;
int actionNumber = FindActionByTypeForLocation( charge->GetPhysics()->GetOrigin(), ACTION_HE_CHARGE, playerTeam );
if ( actionNumber != ACTION_NULL ) {
actionAreaNum = botActions[ actionNumber ]->areaNum;
travelFlags = ( playerTeam == GDF ) ? TFL_VALID_GDF : TFL_VALID_STROGG;
travelTime;
actionOrigin = botActions[ actionNumber ]->GetActionOrigin();
canReach = aas->RouteToGoalArea( actionAreaNum, actionOrigin, areaNum, travelFlags, travelTime, &reach );
if ( botActions[ actionNumber ]->ActionIsPriority() ) {
GetGameWorldState()->chargeInfo[ j ].isOnObjective = true;
}
}
if ( areaNum == 0 || !canReach ) {
idVec3 bombOrigin = charge->GetPhysics()->GetOrigin();
idMat3 bombAxis = charge->GetPhysics()->GetAxis();
idVec3 projOrigin = bombOrigin += ( HALF_PLAYER_BBOX * bombAxis[ 0 ] );
areaNum = aas->PointReachableAreaNum( bombOrigin, aas->GetSettings()->boundingBox, AAS_AREA_REACHABLE_WALK, excludeTravelFlags );
if ( actionNumber != ACTION_NULL ) {
canReach = aas->RouteToGoalArea( actionAreaNum, actionOrigin, areaNum, travelFlags, travelTime, &reach );
}
if ( areaNum == 0 || !canReach ) { //mal: failed, now scan left and right in 2 half_bbox size steps
bombOrigin = projOrigin;
bombOrigin += ( HALF_PLAYER_BBOX * bombAxis[ 1 ] * -1 );
areaNum = aas->PointReachableAreaNum( bombOrigin, aas->GetSettings()->boundingBox, AAS_AREA_REACHABLE_WALK, excludeTravelFlags );
if ( actionNumber != ACTION_NULL ) {
canReach = aas->RouteToGoalArea( actionAreaNum, actionOrigin, areaNum, travelFlags, travelTime, &reach );
}
if ( areaNum == 0 || !canReach ) {
bombOrigin += ( HALF_PLAYER_BBOX * bombAxis[ 1 ] * -1 );
areaNum = aas->PointReachableAreaNum( bombOrigin, aas->GetSettings()->boundingBox, AAS_AREA_REACHABLE_WALK, excludeTravelFlags );
if ( actionNumber != ACTION_NULL ) {
canReach = aas->RouteToGoalArea( actionAreaNum, actionOrigin, areaNum, travelFlags, travelTime, &reach );
}
if ( areaNum == 0 || !canReach ) {
bombOrigin = projOrigin;
bombOrigin += ( -HALF_PLAYER_BBOX * bombAxis[ 1 ] * -1 );
areaNum = aas->PointReachableAreaNum( bombOrigin, aas->GetSettings()->boundingBox, AAS_AREA_REACHABLE_WALK, excludeTravelFlags );
if ( actionNumber != ACTION_NULL ) {
canReach = aas->RouteToGoalArea( actionAreaNum, actionOrigin, areaNum, travelFlags, travelTime, &reach );
}
if ( areaNum == 0 || !canReach ) {
bombOrigin += ( -HALF_PLAYER_BBOX * bombAxis[ 1 ] * -1 );
areaNum = aas->PointReachableAreaNum( bombOrigin, aas->GetSettings()->boundingBox, AAS_AREA_REACHABLE_WALK, excludeTravelFlags );
if ( actionNumber != ACTION_NULL ) {
canReach = aas->RouteToGoalArea( actionAreaNum, actionOrigin, areaNum, travelFlags, travelTime, &reach );
}
if ( areaNum == 0 || !canReach ) { //mal: we're in trouble!
areaNum = 0;
}
}
}
}
}
}
GetGameWorldState()->chargeInfo[ j ].areaNum = areaNum;
GetGameWorldState()->chargeInfo[ j ].checkedAreaNum = true;
}
} else {
GetGameWorldState()->chargeInfo[ j ].entNum = 0;
GetGameWorldState()->chargeInfo[ j ].state = BOMB_NULL;
GetGameWorldState()->chargeInfo[ j ].origin = vec3_zero;
GetGameWorldState()->chargeInfo[ j ].team = NOTEAM;
GetGameWorldState()->chargeInfo[ j ].ownerSpawnID = -1;
GetGameWorldState()->chargeInfo[ j ].ownerEntNum = -1;
GetGameWorldState()->chargeInfo[ j ].spawnID = -1;
GetGameWorldState()->chargeInfo[ j ].areaNum = 0;
GetGameWorldState()->chargeInfo[ j ].checkedAreaNum = false;
if ( bot_debug.GetBool() ) {
gameLocal.DWarning( "DynamicEntity_Think called a non-existent charge!" );
}
}
}
//mal: track carryable objectives next
if( aas != NULL ) {
for( i = 0; i < MAX_CARRYABLES; i++ ) {
if ( GetGameWorldState()->botGoalInfo.carryableObjs[ i ].entNum <= 0 ) {
continue;
}
idEntity *carryableObjective = gameLocal.entities[ GetGameWorldState()->botGoalInfo.carryableObjs[ i ].entNum ];
if ( carryableObjective != NULL && gameLocal.GetSpawnId( carryableObjective ) == GetGameWorldState()->botGoalInfo.carryableObjs[ i ].spawnID ) {
GetGameWorldState()->botGoalInfo.carryableObjs[ i ].origin = carryableObjective->GetPhysics()->GetOrigin();
GetGameWorldState()->botGoalInfo.carryableObjs[ i ].areaNum = aas->PointReachableAreaNum( carryableObjective->GetPhysics()->GetOrigin(), aas->GetSettings()->boundingBox, AAS_AREA_REACHABLE_WALK, 0 );
if ( bot_debug.GetBool() ) {
idVec4 colorType = ( GetGameWorldState()->botGoalInfo.carryableObjs[ i ].areaNum == 0 ) ? colorRed : colorGreen;
gameRenderWorld->DebugBounds( colorType, carryableObjective->GetPhysics()->GetAbsBounds() );
}
} else {
if ( bot_debug.GetBool() ) {
gameLocal.DWarning( "DynamicEntity_Think called a non-existent carryable objective!" );
}
}
}
}
//mal: track smoke grenades next
for( j = 0; j < MAX_CLIENTS; j++ ) {
if ( GetGameWorldState()->smokeGrenades[ j ].entNum <= 0 ) {
continue;
}
idEntity *smokeNade = gameLocal.entities[ GetGameWorldState()->smokeGrenades[ j ].entNum ];
if ( smokeNade != NULL && gameLocal.GetSpawnId( smokeNade ) == GetGameWorldState()->smokeGrenades[ j ].spawnID ) {
GetGameWorldState()->smokeGrenades[ j ].origin = smokeNade->GetPhysics()->GetOrigin();
GetGameWorldState()->smokeGrenades[ j ].xySpeed = smokeNade->GetPhysics()->GetLinearVelocity().LengthFast();
} else {
GetGameWorldState()->smokeGrenades[ j ].entNum = 0;
GetGameWorldState()->smokeGrenades[ j ].spawnID = -1;
if ( bot_debug.GetBool() ) {
gameLocal.DWarning( "DynamicEntity_Think called a non-existent smoke grenade!" );
}
}
}
//mal: track grenades next
for( j = 0; j < MAX_CLIENTS; j++ ) {
for( i = 0; i < MAX_GRENADES; i++ ) {
if ( GetGameWorldState()->clientInfo[ j ].weapInfo.grenades[ i ].entNum <= 0 ) {
continue;
}
idEntity *nade = gameLocal.entities[ GetGameWorldState()->clientInfo[ j ].weapInfo.grenades[ i ].entNum ];
if ( nade != NULL && gameLocal.GetSpawnId( nade ) == GetGameWorldState()->clientInfo[ j ].weapInfo.grenades[ i ].spawnID ) {
GetGameWorldState()->clientInfo[ j ].weapInfo.grenades[ i ].origin = nade->GetPhysics()->GetOrigin();
GetGameWorldState()->clientInfo[ j ].weapInfo.grenades[ i ].xySpeed = nade->GetPhysics()->GetLinearVelocity().LengthFast();
} else {
GetGameWorldState()->clientInfo[ j ].weapInfo.grenades[ i ].entNum = 0;
GetGameWorldState()->clientInfo[ j ].weapInfo.grenades[ i ].spawnID = -1;
if ( bot_debug.GetBool() ) {
gameLocal.DWarning( "DynamicEntity_Think called a non-existent grenade!\n" );
}
}
}
}
//mal: track spawnhosts next
for( j = 0; j < MAX_SPAWNHOSTS; j++ ) {
if ( GetGameWorldState()->spawnHosts[ j ].entNum <= 0 ) {
continue;
}
idEntity *spawnHost = gameLocal.entities[ GetGameWorldState()->spawnHosts[ j ].entNum ];
if ( spawnHost != NULL && gameLocal.GetSpawnId( spawnHost ) == GetGameWorldState()->spawnHosts[ j ].spawnID ) {
if ( !GetGameWorldState()->spawnHosts[ j ].areaChecked ) {
GetGameWorldState()->spawnHosts[ j ].areaNum = Nav_GetAreaNum( AAS_PLAYER, GetGameWorldState()->spawnHosts[ j ].origin );
}
} else {
GetGameWorldState()->spawnHosts[ j ].entNum = 0;
GetGameWorldState()->spawnHosts[ j ].spawnID = -1;
GetGameWorldState()->spawnHosts[ j ].origin = vec3_zero;
GetGameWorldState()->spawnHosts[ j ].areaChecked = false;
GetGameWorldState()->spawnHosts[ j ].areaNum = 0;
if ( bot_debug.GetBool() ) {
gameLocal.DWarning( "DynamicEntity_Think called a non-existent spawnhost!\n" );
}
}
}
//mal: track landmines next
for( j = 0; j < MAX_CLIENTS; j++ ) {
for( i = 0; i < MAX_MINES; i++ ) {
if ( GetGameWorldState()->clientInfo[ j ].weapInfo.landMines[ i ].entNum <= 0 ) {
continue;
}
idEntity *mine = gameLocal.entities[ GetGameWorldState()->clientInfo[ j ].weapInfo.landMines[ i ].entNum ];
if ( mine != NULL && gameLocal.GetSpawnId( mine ) == GetGameWorldState()->clientInfo[ j ].weapInfo.landMines[ i ].spawnID ) {
GetGameWorldState()->clientInfo[ j ].weapInfo.landMines[ i ].origin = mine->GetPhysics()->GetOrigin();
GetGameWorldState()->clientInfo[ j ].weapInfo.landMines[ i ].xySpeed = mine->GetPhysics()->GetLinearVelocity().LengthFast();
GetGameWorldState()->clientInfo[ j ].weapInfo.landMines[ i ].bbox = mine->GetPhysics()->GetAbsBounds();
if ( AllowDebugData() ) {
idBox bbox = idBox( mine->GetPhysics()->GetAbsBounds() );
}
} else {
GetGameWorldState()->clientInfo[ j ].weapInfo.landMines[ i ].entNum = 0;
GetGameWorldState()->clientInfo[ j ].weapInfo.landMines[ i ].spawnID = -1;
GetGameWorldState()->clientInfo[ j ].weapInfo.landMines[ i ].state = BOMB_NULL;
GetGameWorldState()->clientInfo[ j ].weapInfo.landMines[ i ].spotted = false;
GetGameWorldState()->clientInfo[ j ].weapInfo.landMines[ i ].xySpeed = 0.0f;
if ( bot_debug.GetBool() ) {
gameLocal.DWarning( "DynamicEntity_Think called a non-existent landmine!\n" );
}
}
}
}
//mal: track deployables next.
sdTeamManagerLocal& manager = sdTeamManager::GetInstance();
int numTeam0 = 0;
idList< int > deployableActions;
if ( nextDeployableUpdateTime < gameLocal.time ) {
nextDeployableUpdateTime = gameLocal.time + 5000;
for( i = 0; i < botActions.Num(); i++ ) {
if ( botActions[ i ]->GetBaseActionType() != BASE_ACTION ) {
continue;
}
if ( botActions[ i ]->GetBaseObjForTeam( GDF ) != ACTION_DROP_DEPLOYABLE && botActions[ i ]->GetBaseObjForTeam( STROGG ) != ACTION_DROP_DEPLOYABLE &&
botActions[ i ]->GetBaseObjForTeam( GDF ) != ACTION_DROP_PRIORITY_DEPLOYABLE && botActions[ i ]->GetBaseObjForTeam( STROGG ) != ACTION_DROP_PRIORITY_DEPLOYABLE ) {
continue;
}
deployableActions.Append( i );
}
}
for( j = 0; j < MAX_DEPLOYABLES; j++ ) {
deployableInfo_t &deployableInfo = GetGameWorldState()->deployableInfo[ j ];
deployableInfo.entNum = 0;
deployableInfo.inPlace = false;
const idList< idEntityPtr< idEntity > > *deployableList = &manager.GetTeamByIndex( 0 ).GetDeployables();
if ( j >= deployableList->Num() ) {
deployableList = &manager.GetTeamByIndex( 1 ).GetDeployables();
numTeam0 = manager.GetTeamByIndex( 0 ).GetDeployables().Num();
}
if ( ( j - numTeam0 ) >= deployableList->Num() ) {
continue;
}
const idEntityPtr< idEntity >& deployablePtr = ( *deployableList )[ j - numTeam0 ];
idEntity *deployable = deployablePtr;
if ( deployable == NULL ) {
continue;
}
deployableInfo.spawnID = deployablePtr.GetSpawnId();
sdDefenceTurret* turret = deployable->Cast< sdDefenceTurret >();
if ( turret != NULL ) {
idEntity* turretTarget = turret->GetTargetEntity();
idEntity* turretOwner = turret->GetTurretOwner();
sdTeamInfo* turretTeam = turret->GetGameTeam();
deployableInfo.disabled = turret->IsDisabled();
deployableInfo.inPlace = turret->IsDeployed();
deployableInfo.health = turret->GetHealth();
deployableInfo.maxHealth = turret->GetMaxHealth();
deployableInfo.origin = turret->GetPhysics()->GetOrigin();
deployableInfo.entNum = turret->entityNumber;
deployableInfo.enemyEntNum = turretTarget == NULL ? -1 : turretTarget->entityNumber;
deployableInfo.axis = turret->GetPhysics()->GetAxis();
deployableInfo.maxAttackRange = turret->GetTurretMaxRange();
deployableInfo.minAttackRange = turret->GetTurretMinRange();
deployableInfo.ownerClientNum = turretOwner == NULL ? -1 : turretOwner->entityNumber;
deployableInfo.team = turretTeam == NULL ? NOTEAM : turretTeam->GetBotTeam();
deployableInfo.type = turret->GetTurretType();
deployableInfo.bbox = turret->GetPhysics()->GetBounds();
if ( deployableInfo.inPlace ) {
if ( deployableInfo.ownerClientNum != -1 ) {
deployableInfo.areaNum = Nav_GetAreaNum( AAS_PLAYER, deployableInfo.origin );
deployableInfo.areaNumVehicle = Nav_GetAreaNum( AAS_VEHICLE, deployableInfo.origin );
if ( deployableInfo.type == APT ) {
deployableInfo.dangerAreaBox = idBox( deployableInfo.bbox, deployableInfo.origin, deployableInfo.axis );
deployableInfo.dangerAreaBox.ExpandSelf( 1500.0f, 900.0f, 500.0f );
deployableInfo.dangerAreaBox.TranslateSelf( deployableInfo.dangerAreaBox.GetAxis()[0] * 1700.0f );
}
int actionNumber = GetDeployableActionNumber( deployableActions, deployableInfo.origin, deployableInfo.team, deployableInfo.type );
if ( actionNumber != ACTION_NULL && deployableInfo.ownerClientNum != -1 ) {
const clientInfo_t& deployableOwner = GetGameWorldState()->clientInfo[ deployableInfo.ownerClientNum ];
if ( !botActions[ actionNumber ]->ActionIsActive() && deployableOwner.isBot ) {
idEntity *deployableEnt = gameLocal.entities[ deployableInfo.entNum ];
if ( deployableEnt != NULL ) {
deployableEnt->Damage( NULL, NULL, idVec3( 0.0f, 0.0f, 1.0f ), DAMAGE_FOR_NAME( "damage_deployable_destruct" ), 1.0f, NULL );
}
}
}
}
}
} else { //mal: hammers/DMC/etc
sdScriptEntity* miscDeployable = deployable->Cast< sdScriptEntity >();
if ( miscDeployable != NULL ) {
sdTeamInfo* deployableTeam = miscDeployable->GetGameTeam();
idEntity* deployableOwner = miscDeployable->GetOwner();
deployableInfo.origin = miscDeployable->GetPhysics()->GetOrigin();
deployableInfo.entNum = miscDeployable->entityNumber;
deployableInfo.health = miscDeployable->GetHealth();
deployableInfo.maxHealth = miscDeployable->GetMaxHealth();
deployableInfo.axis = miscDeployable->GetPhysics()->GetAxis();
deployableInfo.enemyEntNum = -1;
deployableInfo.minAttackRange = 0.0f;
deployableInfo.maxAttackRange = miscDeployable->GetDeployableRange();
deployableInfo.bbox = miscDeployable->GetPhysics()->GetBounds();
deployableInfo.disabled = miscDeployable->GetIsDisabled();
deployableInfo.inPlace = miscDeployable->GetIsDeployed();
deployableInfo.ownerClientNum = deployableOwner == NULL ? -1 : deployableOwner->entityNumber;
deployableInfo.team = deployableTeam == NULL ? NOTEAM : deployableTeam->GetBotTeam();
deployableInfo.type = miscDeployable->GetDeployableType();
if ( deployableInfo.inPlace ) {
if ( deployableInfo.ownerClientNum != -1 ) {
deployableInfo.areaNum = Nav_GetAreaNum( AAS_PLAYER, deployableInfo.origin );
deployableInfo.areaNumVehicle = Nav_GetAreaNum( AAS_VEHICLE, deployableInfo.origin );
int actionNumber = GetDeployableActionNumber( deployableActions, deployableInfo.origin, deployableInfo.team, deployableInfo.type );
if ( actionNumber != ACTION_NULL && deployableInfo.ownerClientNum != -1 ) {
const clientInfo_t& deployableOwner = GetGameWorldState()->clientInfo[ deployableInfo.ownerClientNum ];
if ( !botActions[ actionNumber ]->ActionIsActive() && deployableOwner.isBot ) {
idEntity *deployableEnt = gameLocal.entities[ deployableInfo.entNum ];
if ( deployableEnt != NULL ) {
deployableEnt->Damage( NULL, NULL, idVec3( 0.0f, 0.0f, 1.0f ), DAMAGE_FOR_NAME( "damage_deployable_destruct" ), 1.0f, NULL );
}
}
}
}
}
}
}
if ( AllowDebugData() ) {
idBox bbox = idBox( deployableInfo.bbox, deployableInfo.origin, deployableInfo.axis );
gameRenderWorld->DebugBox( colorWhite, bbox );
if ( turret != NULL && turret->GetTurretType() == APT ) {
gameRenderWorld->DebugBox( colorRed, deployableInfo.dangerAreaBox );
}
}
}
//mal: vehicles last
sdInstanceCollector< sdTransport > transports( true );
i = 0;
idVec3 aasOrigin;
const float VEHICLE_OWNERSHIP_DIST = 1500.0f;
for ( j = 0; j < MAX_VEHICLES; j++ ) {
GetGameWorldState()->vehicleInfo[ j ].entNum = 0;
GetGameWorldState()->vehicleInfo[ j ].isDeployed = false;
if ( j >= transports.Num() ) {
continue;
}
if ( sdTransport* transport = transports[ j ] ) {
if ( idPhysics* physics = transport->GetPhysics() ) {
proxyInfo_t &vehicleInfo = GetGameWorldState()->vehicleInfo[ i ];
vehicleInfo.bbox = physics->GetBounds( 0 );
for( k = 1; k < physics->GetNumClipModels(); k++ ) {
//mal: if the vehicle has multiple bounding box, add them all together for the overall bounds.
// Gordon: passing -1 should do this
vehicleInfo.bbox.AddBounds( physics->GetBounds( k ) );
}
vehicleInfo.origin = transport->GetRenderEntity()->origin;
vehicleInfo.type = transport->GetVehicleType();
vehicleInfo.team = transport->GetVehicleTeam();
vehicleInfo.flags = transport->GetVehicleFlags();
vehicleInfo.isEmpty = transport->GetPositionManager().IsEmpty();
vehicleInfo.isEMPed = transport->IsEMPed();
vehicleInfo.isFlipped = transport->IsFlipped();
vehicleInfo.isAmphibious = transport->IsAmphibious();
vehicleInfo.inWater = ( physics->InWater() > 0.0f ) ? true: false;
vehicleInfo.health = transport->GetHealth();
vehicleInfo.maxHealth = transport->GetMaxHealth();
vehicleInfo.axis = transport->GetRenderEntity()->axis;
vehicleInfo.isAirborneVehicle = ( vehicleInfo.type >= ICARUS ) ? true : false;
vehicleInfo.isAirborneAttackVehicle = ( vehicleInfo.type == ANANSI || vehicleInfo.type == HORNET ) ? true : false;
vehicleInfo.inPlayZone = transport->IsInPlayzone();
vehicleInfo.hasFreeSeat = transport->GetPositionManager().HasFreePosition();
vehicleInfo.inSiegeMode = ( transport->GetVehicleControl() && transport->GetVehicleControl()->InSiegeMode() ) ? 1 : 0;
vehicleInfo.hasGroundContact = physics->HasGroundContacts();
vehicleInfo.damagedPartsCount = transport->GetDestroyedCriticalDriveParts();
vehicleInfo.isCareening = transport->IsCareening();
vehicleInfo.isImmobilized = ( transport->GetVehicleControl() && transport->GetVehicleControl()->IsImmobilized() ) ? 1 : 0;
vehicleInfo.wheelsAreOnGround = transport->AreWheelsOnGround();
vehicleInfo.canRotateInPlace = false;
vehicleInfo.actionRouteNumber = transport->GetRouteActionNumber();
vehicleInfo.isBoobyTrapped = false;
idEntity* next = NULL;
playerTeamTypes_t ignoreTeam = transport->GetVehicleTeam();
if ( si_teamDamage.GetBool() ) {
ignoreTeam = NOTEAM;
}
for ( idEntity* other = transport->GetNextTeamEntity(); other; other = next ) {
next = other->GetNextTeamEntity();
if ( other == transport ) {
continue;
}
if ( EntityIsExplosiveCharge( other->entityNumber, true, ignoreTeam ) ) {
vehicleInfo.isBoobyTrapped = true;
}
}
if ( vehicleInfo.type == GOLIATH || vehicleInfo.type == MCP || vehicleInfo.type == TITAN || vehicleInfo.type == DESECRATOR ) {
vehicleInfo.canRotateInPlace = true;
}
idPlayer* driver = transport->GetPositionManager().FindDriver();
if ( driver != NULL ) {
vehicleInfo.driverEntNum = driver->entityNumber;
} else {
vehicleInfo.driverEntNum = -1;
}
vehicleInfo.isOwned = false;
vehicleInfo.neverDriven = false;
vehicleInfo.spawnID = gameLocal.GetSpawnId( transport );
idPlayer* player = transport->GetLastOccupant();
if ( player != NULL ) {
if ( player->ownsVehicle != false && ( player->GetProxyEntity() != NULL && player->GetProxyEntity()->entityNumber == transport->entityNumber ) ) { //mal: if player exited vehicle and was killed, or is too far away, bot can take it.
idVec3 vec;
vec = player->GetPhysics()->GetOrigin() - vehicleInfo.origin;
if ( vec.LengthSqr() < Square( VEHICLE_OWNERSHIP_DIST ) ) {
if ( player->GetProxyEntity() != NULL ) {
if ( vehicleInfo.driverEntNum == player->entityNumber ) {
vehicleInfo.isOwned = true;
vehicleInfo.ownerNum = player->entityNumber; //mal: if the player is in the vehicle, but NOT the driver, bot can take control!
}
} else {
vehicleInfo.isOwned = true;
vehicleInfo.ownerNum = player->entityNumber;
}
}
} else if ( player->ownsVehicle != false && gameLocal.GetSpawnId( transport ) == player->lastOwnedVehicleSpawnID ) {
idVec3 vec;
vec = player->GetPhysics()->GetOrigin() - vehicleInfo.origin;
if ( vec.LengthSqr() < Square( VEHICLE_OWNERSHIP_DIST ) ) {
vehicleInfo.isOwned = true;
vehicleInfo.ownerNum = player->entityNumber; //mal: if the player is in the vehicle, but NOT the driver, bot can take control!
}
}
} else {
vehicleInfo.neverDriven = true;
} //mal: dont steal vehicles from other players ( bots and humans ) Will ignore this for important vehicles ( MCP, etc ).
vehicleInfo.entNum = transport->entityNumber;
vehicleInfo.xyspeed = physics->GetLinearVelocity().LengthFast();
vehicleInfo.forwardSpeed = transport->GetRenderEntity()->axis[ 0 ] * physics->GetLinearVelocity();
if ( vehicleInfo.type == MCP ) {
vehicleInfo.isDeployed = transport->IsDeployed();
}
aasOrigin = physics->GetOrigin();
areaNum = Nav_GetAreaNumAndOrigin( AAS_PLAYER, physics->GetOrigin(), aasOrigin );
if ( areaNum > 0 ) { //mal: cache last valid area
vehicleInfo.areaNum = areaNum;
vehicleInfo.aasOrigin = aasOrigin;
}
aasOrigin = physics->GetOrigin();
areaNum = Nav_GetAreaNumAndOrigin( AAS_VEHICLE, physics->GetOrigin(), aasOrigin );
if ( areaNum > 0 ) {
vehicleInfo.areaNumVehicle = areaNum;
vehicleInfo.aasVehicleOrigin = aasOrigin;
}
if ( vehicleInfo.type == MCP ) {
GetGameWorldState()->botGoalInfo.botGoal_MCP_VehicleNum = vehicleInfo.entNum;
}
i++;
}
}
}
if ( !forwardSpawnsChecked ) {
forwardSpawnsChecked = true;
bool foundSpawnPoint = true;
sdInstanceCollector< sdSpawnController > forwardSpawns( false );
if ( forwardSpawns.Num() > 0 ) {
idList< int > forwardSpawnActions;
for( i = 0; i < botActions.Num(); i++ ) {
if ( botActions[ i ]->GetBaseActionType() != BASE_ACTION ) {
continue;
}
if ( botActions[ i ]->GetBaseObjForTeam( GDF ) != ACTION_DENY_SPAWNPOINT && botActions[ i ]->GetBaseObjForTeam( STROGG ) != ACTION_DENY_SPAWNPOINT &&
botActions[ i ]->GetBaseObjForTeam( GDF ) != ACTION_FORWARD_SPAWN && botActions[ i ]->GetBaseObjForTeam( STROGG ) != ACTION_FORWARD_SPAWN ) {
continue;
}
forwardSpawnActions.Append( i );
}
if ( forwardSpawnActions.Num() > 0 ) {
foundSpawnPoint = false; //mal: only report error if spawns and actions exist.
for( i = 0; i < forwardSpawnActions.Num(); i++ ) {
for( k = 0; k < forwardSpawns.Num(); k++ ) {
const sdSpawnController* spawnPoint = forwardSpawns[ k ];
if ( spawnPoint == NULL ) {
continue;
}
idBox actionBox = botActions[ forwardSpawnActions[ i ] ]->actionBBox;
actionBox.ExpandSelf( ACTION_BBOX_EXPAND_BIG );
if ( !actionBox.ContainsPoint( spawnPoint->GetPhysics()->GetOrigin() ) ) {
continue;
}
botActions[ forwardSpawnActions[ i ] ]->spawnControllerEntityNum = spawnPoint->entityNumber;
foundSpawnPoint = true;
break;
}
}
}
}
if ( !foundSpawnPoint ) {
if ( botThreadData.AllowDebugData() ) {
gameLocal.Warning("No sdSpawnController entity found thats matches a bot foward_spawn action.\nThis could be because there are no forward spawn actions setup for the bots\nOr that the forward spawn action's bbox isn't correctly setup.");
}
}
}
}
/*
================
idBotThreadData::LoadActions
Parse the bot actions and load them up.
================
*/
void idBotThreadData::LoadActions( const idMapFile *mapFile ) {
int obstacleCounter = MAX_CLIENTS + MAX_GENTITIES; // MAX_GENTITIES -> MAX_GENTITIES + MAX_CLIENTS is used for arty obstacles.
idToken token;
idVec3 extents;
idAngles angles;
idVec3 center;
for ( int i = 0; i < mapFile->GetNumEntities(); i++ ) {
idMapEntity* mapEnt = mapFile->GetEntity( i );
const char* classname = mapEnt->epairs.GetString( "classname" );
if ( idStr::Icmp( classname, "bot_action_bbox" ) == 0 ) {
idBotActions* actionLoader = new idBotActions;
actionLoader->name = mapEnt->epairs.GetString( "name", "" );
mapEnt->epairs.GetVector( "box_center", "0 0 0", center );
mapEnt->epairs.GetVector( "box_extents", "0 0 0", extents );
mapEnt->epairs.GetAngles( "box_angles", "0 0 0", angles );
actionLoader->actionBBox = idBox( center, extents, angles.ToMat3() );
actionLoader->targetAction = mapEnt->epairs.GetString( "target", "" );
if ( actionLoader->targetAction.Length() == 0 ) {
gameLocal.Error( "idBotThreadData::LoadActions - action %s doesn't point to a valid bot_action\nHalting action loading!!", actionLoader->name.c_str() );
return;
}
actionLoader->baseActionType = BBOX_ACTION;
botActions.Append( actionLoader );
continue;
}
if ( idStr::Icmp( classname, "bot_action_target" ) == 0 ) {
idBotActions* actionLoader = new idBotActions;
actionLoader->name = mapEnt->epairs.GetString( "name", "" );
mapEnt->epairs.GetVector( "origin", "", actionLoader->origin );
actionLoader->radius = mapEnt->epairs.GetInt( "radius", "70.0f" );
actionLoader->targetAction = mapEnt->epairs.GetString( "target", "" );
if ( actionLoader->targetAction.Length() == 0 ) {
gameLocal.Error( "idBotThreadData::LoadActions - action %s doesn't point to a valid bot_action\nHalting action loading!!", actionLoader->name.c_str() );
return;
}
actionLoader->baseActionType = TARGET_ACTION;
botActions.Append( actionLoader );
continue;
}
if ( idStr::Icmp( classname, "bot_action" ) == 0 ) {
idBotActions* actionLoader = new idBotActions;
actionLoader->name = mapEnt->epairs.GetString( "name", "" );
actionLoader->targetAction = mapEnt->epairs.GetString( "target", "" );
if ( actionLoader->targetAction.Length() != 0 ) {
gameLocal.Error( "idBotThreadData::LoadActions - action %s can not target other actions. \"BBox\" or \"Target\" actions must target \"bot_action\" entities instead!\nHalting action loading!!", actionLoader->name.c_str() );
return; //mal: make sure someone isn't being silly.
}
actionLoader->activeForever = mapEnt->epairs.GetBool( "activeForever", "0" );
actionLoader->validClasses = mapEnt->epairs.GetBool( "validClasses", "0" );
actionLoader->active = mapEnt->epairs.GetBool( "active", "0" );
actionLoader->radius = mapEnt->epairs.GetInt( "radius", "120.0f" );
actionLoader->deployableType = mapEnt->epairs.GetInt( "deployableType", "0" );
actionLoader->requiresVehicleType = mapEnt->epairs.GetBool( "requiresVehicle", "0" );
actionLoader->routeID = mapEnt->epairs.GetInt( "targetRouteID", "-1" );
actionLoader->blindFire = mapEnt->epairs.GetBool( "blindFire", "0" );
actionLoader->priority = mapEnt->epairs.GetBool( "priority", "1" );
actionLoader->noHack = mapEnt->epairs.GetBool( "noHack", "0" );
actionLoader->VOChatFlag = ( botChatTypes_t ) mapEnt->epairs.GetInt( "VOChat", "-1" );
mapEnt->epairs.GetVector( "origin", "", actionLoader->origin );
actionLoader->humanObj = ( botActionGoals_t ) mapEnt->epairs.GetInt( "humanGoal", "-1" );
actionLoader->stroggObj = ( botActionGoals_t ) mapEnt->epairs.GetInt( "stroggGoal", "-1" );
actionLoader->baseHumanObj = actionLoader->humanObj;
actionLoader->baseStroggObj = actionLoader->stroggObj;
actionLoader->teamOwner = ( playerTeamTypes_t ) mapEnt->epairs.GetInt( "teamOwner", "-1" );
actionLoader->leanDir = ( leanTypes_t ) mapEnt->epairs.GetInt( "leanDir", "-1" );
actionLoader->baseActionType = BASE_ACTION;
actionLoader->groupID = mapEnt->epairs.GetInt( "groupID", "0" );
actionLoader->posture = ( botMoveFlags_t ) mapEnt->epairs.GetInt( "posture", "2" ); //stand
actionLoader->actionTimeInSeconds = mapEnt->epairs.GetInt( "actionTime", "30" );
actionLoader->actionVehicleFlags = mapEnt->epairs.GetInt( "vehicleType", "-1" );
int tempWeap = mapEnt->epairs.GetInt( "weaponType", "-1" );
if ( tempWeap == 0 ) {
actionLoader->weapType = HEAVY_MG;
} else if ( tempWeap == 1 ) {
actionLoader->weapType = ROCKET;
} else {
actionLoader->weapType = NULL_WEAP;
}
actionLoader->areaNum = Nav_GetAreaNum( AAS_PLAYER, actionLoader->origin );
actionLoader->areaNumVehicle = Nav_GetAreaNum( AAS_VEHICLE, actionLoader->origin );
if ( actionLoader->humanObj == ACTION_MG_NEST_BUILD || actionLoader->stroggObj == ACTION_MG_NEST_BUILD ) {
actionLoader->actionState = ACTION_STATE_GUN_DESTROYED;
} else {
actionLoader->actionState = ACTION_STATE_NORMAL;
}
actionLoader->hasObj = false;
AddActionToHash( actionLoader->name, botActions.Append( actionLoader ) );
continue;
}
if ( idStr::Icmp( classname, "bot_dynamic_obstacle" ) == 0 ) {
idBotObstacle* obstacle = new idBotObstacle;
obstacle->num = obstacleCounter++;
mapEnt->epairs.GetVector( "box_center", "0 0 0", center );
mapEnt->epairs.GetVector( "box_extents", "0 0 0", extents );
mapEnt->epairs.GetAngles( "box_angles", "0 0 0", angles );
obstacle->bbox = idBox( center, extents, angles.ToMat3() );
obstacle->areaNum[AAS_PLAYER] = Nav_GetAreaNum( AAS_PLAYER, center );
obstacle->areaNum[AAS_VEHICLE] = Nav_GetAreaNum( AAS_VEHICLE, center );
if ( obstacle->areaNum[AAS_PLAYER] == 0 ) {
//gameLocal.Warning( "bot obstacle at '%1.0f %1.0f %1.0f' is not in a valid player AAS area", center.x, center.y, center.z );
}
if ( obstacle->areaNum[AAS_VEHICLE] == 0 ) {
//gameLocal.Warning( "bot obstacle at '%1.0f %1.0f %1.0f' is not in a valid vehicle AAS area", center.x, center.y, center.z );
}
botObstacles.Append( obstacle );
continue;
}
if ( idStr::Icmp( classname, "bot_locationremap" ) == 0 ) {
botLocationRemap_t *remap = new botLocationRemap_t;
mapEnt->epairs.GetVector( "box_center", "0 0 0", center );
mapEnt->epairs.GetVector( "box_extents", "0 0 0", extents );
mapEnt->epairs.GetAngles( "box_angles", "0 0 0", angles );
remap->bbox = idBox( center, extents, angles.ToMat3() );
idMapEntity *targetEnt = mapFile->FindEntity( mapEnt->epairs.GetString( "target", "" ) );
if ( targetEnt == NULL ) {
gameLocal.Error( "idBotThreadData::LoadActions - bot_locationremap %s doesn't point to a valid bot_locationremap_target!!", mapEnt->epairs.GetString( "name" ) );
return;
}
targetEnt->epairs.GetVector( "origin", "0 0 0", remap->target );
botLocationRemap.Append( remap );
continue;
}
}
//mal: lets go thru all the actions, and find the actions that are targeting base actions ( if any ).
for ( int i = 0; i < botActions.Num(); i++ ) {
if ( botActions[ i ]->baseActionType == BASE_ACTION ) {
continue;
}
if ( botActions[ i ]->baseActionType == BBOX_ACTION ) {
int t = FindActionByName( botActions[ i ]->targetAction );
if ( t == -1 ) {
gameLocal.Error( "idBotThreadData::LoadActions - bbox action %s doesn't point to a valid bot_action\nAction %s either doesn't exist, or isn't a valid primary action\nHalting action loading!!", botActions[ i ]->name.c_str(), botActions[ i ]->targetAction.c_str() );
return;
}
if ( botActions[ t ]->baseActionType != BASE_ACTION ) {
gameLocal.Error( "idBotThreadData::LoadActions - base action %s isn't a valid bot_action\nHalting action loading!!", botActions[ t ]->name.c_str() );
return;
}
botActions[ t ]->actionBBox = botActions[ i ]->actionBBox;
}
if ( botActions[ i ]->baseActionType == TARGET_ACTION ) {
bool foundSlot = false;
int t = FindActionByName( botActions[ i ]->targetAction );
if ( t == -1 ) {
gameLocal.Error( "idBotThreadData::LoadActions - target action %s doesn't point to a valid bot_action\nAction %s either doesn't exist, or isn't a valid primary action\nHalting action loading!!", botActions[ i ]->name.c_str(), botActions[ i ]->targetAction.c_str() );
return;
}
if ( botActions[ t ]->baseActionType != BASE_ACTION ) {
gameLocal.Error( "idBotThreadData::LoadActions - base action %s isn't a valid bot_action\nHalting action loading!!", botActions[ t ]->name.c_str() );
return;
}
for ( int j = 0; j < MAX_LINKACTIONS; j++ ) {
if ( botActions[ t ]->actionTargets[ j ].inuse != false ) {
continue;
}
botActions[ t ]->actionTargets[ j ].inuse = true;
botActions[ t ]->actionTargets[ j ].origin = botActions[ i ]->origin;
botActions[ t ]->actionTargets[ j ].radius = botActions[ i ]->radius;
foundSlot = true;
break;
}
if ( foundSlot == false ) {
gameLocal.Error( "idBotThreadData::LoadActions - too many target actions pointing to base action %s\nMax target actions that can point to one bot_action is %i\nHalting action loading!!", botActions[ t ]->name.c_str(), MAX_LINKACTIONS );
return;
}
}
}
if ( botActions.Num() > 0 ) {
actionsLoaded = true;
}
DeactivateBadMapActions();
}
/*
================
idBotThreadData::RemapLocation
================
*/
bool idBotThreadData::RemapLocation( idVec3 &origin ) const {
for ( int i = 0; i < botLocationRemap.Num(); i++ ) {
if ( botLocationRemap[i]->bbox.ContainsPoint( origin ) ) {
origin = botLocationRemap[i]->target;
return true;
}
}
return false;
}
/*
================
idBotThreadData::LoadRoutes
Parse the bot routes and load them up.
================
*/
void idBotThreadData::LoadRoutes( const idMapFile *mapFile ) {
int i, j, k;
idBotRoutes *routeLoader;
for( i = 0; i < mapFile->GetNumEntities(); i++ ) {
const idDict &epairs = mapFile->GetEntity( i )->epairs;
if ( idStr::Icmp( epairs.GetString( "classname" ), "bot_route_start" ) == 0 ) {
routeLoader = new idBotRoutes;
epairs.GetVector( "origin", "", routeLoader->origin );
routeLoader->radius = epairs.GetInt( "radius", "120.0f" );
routeLoader->team = ( playerTeamTypes_t ) epairs.GetInt( "team", "-1" );
routeLoader->isHeadNode = true;
routeLoader->active = epairs.GetBool( "active", "1" );
routeLoader->groupID = epairs.GetInt( "targetRouteID", "-1" );
routeLoader->name = epairs.GetString( "name", "" );
routeLoader->num = botRoutes.Num();
AddRouteToHash( routeLoader->name, botRoutes.Append( routeLoader ) );
continue;
}
if ( idStr::Icmp( epairs.GetString( "classname" ), "bot_route_link" ) == 0 ) {
routeLoader = new idBotRoutes;
epairs.GetVector( "origin", "", routeLoader->origin );
routeLoader->radius = epairs.GetInt( "radius", "120.0f" );
routeLoader->team = ( playerTeamTypes_t ) epairs.GetInt( "team", "-1" );
routeLoader->isHeadNode = false;
routeLoader->active = epairs.GetBool( "active", "1" );
routeLoader->groupID = epairs.GetInt( "targetRouteID", "-1" );
routeLoader->name = epairs.GetString( "name", "" );
routeLoader->num = botRoutes.Num();
AddRouteToHash( routeLoader->name, botRoutes.Append( routeLoader ) );
continue;
}
}
//mal: now go thru again and find the routes that are linked together...
for( i = 0; i < mapFile->GetNumEntities(); i++ ) {
const idDict &epairs = mapFile->GetEntity( i )->epairs;
if ( idStr::Icmp( epairs.GetString( "classname" ), "bot_route_start" ) != 0 && idStr::Icmp( epairs.GetString( "classname" ), "bot_route_link" ) != 0 ) {
continue;
}
const idKeyValue* kv = epairs.MatchPrefix( "target" );
const char* routeName = epairs.GetString( "name", "" );
while ( kv != NULL ) {
for( j = 0; j < botRoutes.Num(); j++ ) {
if ( idStr::Icmp( botRoutes[ j ]->name, routeName ) == 0 ) {
break;
}
}
assert( j < botRoutes.Num() );
for( k = 0; k < botRoutes.Num(); k++ ) {
if ( k == j ) {
continue;
}
if ( idStr::Icmp( botRoutes[ k ]->name, kv->GetValue().c_str() ) != 0 ) {
continue;
}
botRoutes[ j ]->routeLinks.Append( botRoutes[ k ] );
break;
}
kv = epairs.MatchPrefix( "target", kv );
}
}
}
/*
================
idBotThreadData::VOChat
A safe way to say chats from anywhere in the game. If forceChat == true, will say the chat
no matter what.
//mal_FIXME: loop thru all the chats at startup and store off the indexes in a hash.
================
*/
void idBotThreadData::VOChat( const botChatTypes_t chatType, int clientNum, bool forceChat ) {
if ( !botThreadData.ClientIsValid( clientNum ) ) {
return;
}
#ifdef _XENON
if ( gameLocal.rules->IsWarmup() ) {
return;
}
#endif
const sdDeclQuickChat* quickChat;
idPlayer* player;
const char* quickChatName;
if ( bot_noChat.GetBool() ) { //mal: shaddup you mouthy bot!
return;
}
if ( GetGameWorldState()->clientInfo[ clientNum ].chatDelay > gameLocal.time ) {
return;
} //mal: dont allow overlapping chats!
if ( chatType == NULL_CHAT ) {
return;
}
clientInfo_t& playerInfo = botThreadData.GetGameWorldState()->clientInfo[ clientNum ];
quickChat = NULL;
player = gameLocal.GetClient( clientNum );
if ( player == NULL ) {
return;
}
if ( player->GetGameTeam() == NULL ) {
return;
}
const sdDeclPlayerClass* pc = player->GetInventory().GetClass();
if( pc == NULL ) {
return ;
}
if ( chatType == THANKS ) {
if ( playerInfo.lastChatTime[ THANKS ] < gameLocal.time || forceChat ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/responses/thanks" );
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastThanksTime = gameLocal.time;
playerInfo.lastChatTime[ THANKS ] = gameLocal.time + 30000;
}
} else if ( chatType == HEAL_ME ) {
if ( playerInfo.lastChatTime[ HEAL_ME ] < gameLocal.time || forceChat ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/context/health" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/context/stroyent" );
}
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ HEAL_ME ] = gameLocal.time + 15000;
}
} else if ( chatType == NEED_BACKUP ) {
if ( playerInfo.lastChatTime[ NEED_BACKUP ] < gameLocal.time || forceChat ) {
if ( botThreadData.random.RandomInt( 100 ) > 50 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/need/backup" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/need/coveringfire" );
}
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ NEED_BACKUP ] = gameLocal.time + 120000; //mal: REALLY dont want to do this too much!
}
} else if ( chatType == HOLDFIRE ) {
if ( playerInfo.lastChatTime[ HOLDFIRE ] < gameLocal.time || forceChat ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/commands/holdfire" );
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ HOLDFIRE ] = gameLocal.time + 60000;
}
} else if ( chatType == MOVE ) {
if ( playerInfo.lastChatTime[ MOVE ] < gameLocal.time || forceChat ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/commands/move" );
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ MOVE ] = gameLocal.time + 30000;
}
} else if ( chatType == FOLLOW_ME ) {
if ( playerInfo.lastChatTime[ FOLLOW_ME ] < gameLocal.time || forceChat ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/commands/followme" );
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ FOLLOW_ME ] = gameLocal.time + 5000;
}
} else if ( chatType == NEED_LIFT ) {
if ( playerInfo.lastChatTime[ NEED_LIFT ] < gameLocal.time || forceChat ) {
if ( random.RandomInt( 100 ) > 50 ) {
quickChatName = pc->BuildQuickChatDeclName( "botchat/generic/needalift" );
} else {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "botchat/generic/needaride" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "botchat/generic/needtransport" );
}
}
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ NEED_LIFT ] = gameLocal.time + 10000; //mal: dont do for a while
}
} else if ( chatType == CMD_DECLINED && ( lastCmdDeclinedChatTime < gameLocal.time || forceChat ) ) {
if ( playerInfo.lastChatTime[ CMD_DECLINED ] < gameLocal.time || forceChat ) {
if ( random.RandomInt( 100 ) > 50 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/responses/declined" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/responses/unabletoassist" );
}
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ CMD_DECLINED ] = gameLocal.time + REQUEST_CONSIDER_TIME; //mal: dont do for a while
lastCmdDeclinedChatTime = gameLocal.time + 20000; //REQUEST_CONSIDER_TIME;
}
} else if ( chatType == MY_CLASS ) {
if ( playerInfo.lastChatTime[ MY_CLASS ] < gameLocal.time || forceChat ) {
if ( playerInfo.classType == MEDIC ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/self/immedic" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/self/imtechnician" );
}
} else if ( playerInfo.classType == SOLDIER ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/self/imsoldier" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/self/imaggressor" );
}
} else if ( playerInfo.classType == ENGINEER ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/self/imengineer" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/self/imconstructor" );
}
} else if ( playerInfo.classType == FIELDOPS ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/self/imfieldops" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/self/imoppressor" );
}
} else {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/self/imcovertops" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/self/iminfiltrator" );
}
}
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ MY_CLASS ] = gameLocal.time + 30000;
}
} else if ( chatType == REARM_ME ) {
if ( playerInfo.lastChatTime[ REARM_ME ] < gameLocal.time || forceChat ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/need/ammo" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/context/stroyent" );
}
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ REARM_ME ] = gameLocal.time + 15000;
}
} else if ( chatType == REVIVE_ME ) {
if ( playerInfo.lastChatTime[ REVIVE_ME ] < gameLocal.time || forceChat ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/context/revive" );
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ REVIVE_ME ] = gameLocal.time + 30000;
}
} else if ( chatType == ENEMY_DISGUISED ) {
if ( playerInfo.lastChatTime[ ENEMY_DISGUISED ] < gameLocal.time || forceChat ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/enemy/indisguise" );
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ ENEMY_DISGUISED ] = gameLocal.time + 20000;
}
} else if ( chatType == KILLED_TAUNT && !bot_noTaunt.GetBool() ) {
if ( playerInfo.lastChatTime[ KILLED_TAUNT ] < gameLocal.time || forceChat ) {
int n = random.RandomInt( 12 );
if ( n == 0 ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "global/taunts/grr" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "global/taunts/rrr" );
}
} else if ( n == 1 ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/greatshot" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/awesome" );
}
} else if ( n == 2 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/thathurt" );
} else if ( n == 3 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/ihadworse" );
} else if ( n == 4 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/cough" );
} else if ( n == 5 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/meh" );
} else if ( n == 6 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/awkward" );
} else if ( n == 7 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/awyeahohno" );
} else if ( n == 8 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/ohdear" );
} else if ( n == 9 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/oops" );
} else if ( n == 10 ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/bullseye" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/accurate" );
}
} else {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/wellplayed" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/impressive" );
}
}
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ KILLED_TAUNT ] = gameLocal.time + 30000;
}
} else if ( chatType == WARMUP_TAUNT && !bot_noTaunt.GetBool() ) {
if ( playerInfo.lastChatTime[ WARMUP_TAUNT ] < gameLocal.time || forceChat ) {
int n = random.RandomInt( 6 );
if ( n == 0 ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/gdf" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/strogg" );
}
} else if ( n == 1 ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/killalienscum" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/killhumanfood" );
}
} else if ( n == 2 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/ohdear" );
} else if ( n == 3 ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/enemyweakened" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/theycrumble" );
}
} else if ( n == 4 ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/theyrunningaway" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/theyfleeterror" );
}
} else {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/freedomofearth" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/forthemakron" );
}
}
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ WARMUP_TAUNT ] = gameLocal.time + 10000;
}
} else if ( chatType == GENERAL_TAUNT && ( !bot_noTaunt.GetBool() || forceChat ) ) { //mal: make an exception for this chat type - since bots need to taunt when humiliating enemy.
if ( playerInfo.lastChatTime[ GENERAL_TAUNT ] < gameLocal.time || forceChat ) {
int n = random.RandomInt( 6 );
if ( n == 0 ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/freedomofearth" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/forthemakron" );
}
} else if ( n == 1 ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/killalienscum" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/killhumanfood" );
}
} else if ( n == 2 ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/eatthatstrogg" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/eatthathuman" );
}
} else if ( n == 3 ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/gdf" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/strogg" );
}
} else if ( n == 4 ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/denied" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/thwarted" );
}
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/owned" );
}
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ GENERAL_TAUNT ] = gameLocal.time + 10000;
}
} else if ( chatType == WILL_FIX_RIDE ) {
if ( playerInfo.lastChatTime[ WILL_FIX_RIDE ] < gameLocal.time || forceChat ) {
if ( gameLocal.random.RandomInt( 100 ) > 50 ) {
quickChatName = pc->BuildQuickChatDeclName( "botchat/generic/vehicle/letmerepair" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "botchat/generic/vehicle/iwillfixride" );
}
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ WILL_FIX_RIDE ] = gameLocal.time + 15000;
}
} else if ( chatType == STOP_WILL_FIX_RIDE ) {
if ( playerInfo.lastChatTime[ STOP_WILL_FIX_RIDE ] < gameLocal.time || forceChat ) {
quickChatName = pc->BuildQuickChatDeclName( "botchat/generic/vehicle/stopiwillfixride" );
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ STOP_WILL_FIX_RIDE ] = gameLocal.time + 15000;
}
} else if ( chatType == YOURWELCOME ) {
if ( playerInfo.lastChatTime[ YOURWELCOME ] < gameLocal.time || forceChat ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/responses/youwelcome" );
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ YOURWELCOME ] = gameLocal.time + 15000;
}
} else if ( chatType == ACKNOWLEDGE_YES ) {
if ( playerInfo.lastChatTime[ ACKNOWLEDGE_YES ] < gameLocal.time || forceChat ) {
int n = random.RandomInt( 3 );
if ( n == 0 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/responses/acknowledged" );
} else if ( n == 1 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/responses/onit" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/responses/onmyway" );
}
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ ACKNOWLEDGE_YES ] = gameLocal.time + 15000;
}
} else if ( chatType == MEDIC_ACK ) {
if ( playerInfo.lastChatTime[ MEDIC_ACK ] < gameLocal.time || forceChat ) {
int n = random.RandomInt( 3 );
if ( n == 0 ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "botchat/generic/holdonimamedic" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "botchat/generic/holdonimatechnician" );
}
} else if ( n == 1 ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "botchat/generic/medicenroute" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "botchat/generic/technicianenroute" );
}
} else {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/self/immedic" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/self/imtechnician" );
}
}
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ MEDIC_ACK ] = gameLocal.time + 15000;
}
} else if ( chatType == AMMO_ACK ) {
if ( playerInfo.lastChatTime[ AMMO_ACK ] < gameLocal.time || forceChat ) {
quickChatName = pc->BuildQuickChatDeclName( "botchat/generic/gotammo" );
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ AMMO_ACK ] = gameLocal.time + 15000;
}
} else if ( chatType == TK_REVIVE_CHAT ) {
if ( playerInfo.lastChatTime[ TK_REVIVE_CHAT ] < gameLocal.time || forceChat ) {
quickChatName = pc->BuildQuickChatDeclName( "botchat/generic/tkrevive" );
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ TK_REVIVE_CHAT ] = gameLocal.time + 15000; //mal: dont go again for a while.
}
} else if ( chatType == HELLO ) {
if ( playerInfo.lastChatTime[ HELLO ] < gameLocal.time || forceChat ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/hi" );
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ HELLO ] = gameLocal.time + 60000; //mal: dont go again for a while.
}
} else if ( chatType == GOT_YOUR_BACK ) {
if ( playerInfo.lastChatTime[ GOT_YOUR_BACK ] < gameLocal.time || forceChat ) {
quickChatName = pc->BuildQuickChatDeclName( "botchat/generic/gotyourback" );
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ GOT_YOUR_BACK ] = gameLocal.time + 5000; //mal: dont go again for a while.
}
} else if ( chatType == MOVE_OUT ) {
if ( playerInfo.lastChatTime[ MOVE_OUT ] < gameLocal.time || forceChat ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/commands/letsgo" );
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ MOVE_OUT ] = gameLocal.time + 5000; //mal: dont go again for a while.
}
} else if ( chatType == LETS_GO ) {
if ( playerInfo.lastChatTime[ LETS_GO ] < gameLocal.time || forceChat ) {
int n = random.RandomInt( 5 );
if ( n == 0 ) {
quickChatName = pc->BuildQuickChatDeclName( "botchat/generic/youlead" );
} else if ( n == 1 ) {
quickChatName = pc->BuildQuickChatDeclName( "botchat/generic/gotyourback" );
} else if ( n == 2 ) {
quickChatName = pc->BuildQuickChatDeclName( "botchat/generic/leadway" );
} else if ( n == 3 ) {
quickChatName = pc->BuildQuickChatDeclName( "botchat/generic/whereto" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/commands/letsgo" );
}
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ LETS_GO ] = gameLocal.time + 60000; //mal: dont go again for a while.
}
} else if ( chatType == GENERIC_PLANT ) {
if ( playerInfo.lastChatTime[ GENERIC_PLANT ] < gameLocal.time || forceChat ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "botchat/objectives/planthecharge" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "botchat/objectives/plantplasmacharge" );
}
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ GENERIC_PLANT ] = gameLocal.time + 60000; //mal: dont go again for a while.
}
} else if ( chatType == INCOMING_VEHICLE ) {
if ( playerInfo.lastChatTime[ INCOMING_VEHICLE ] < gameLocal.time || forceChat ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/enemy/vehiclespotted" );
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ INCOMING_VEHICLE ] = gameLocal.time + 30000;
}
} else if ( chatType == INCOMING_AIRCRAFT ) {
if ( playerInfo.lastChatTime[ INCOMING_AIRCRAFT ] < gameLocal.time || forceChat ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/enemy/aircraftspotted" );
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ INCOMING_AIRCRAFT ] = gameLocal.time + 30000;
}
}else if ( chatType == INCOMING_INFANTRY ) {
if ( playerInfo.lastChatTime[ INCOMING_INFANTRY ] < gameLocal.time || forceChat ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/enemy/infantryspotted" );
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ INCOMING_INFANTRY ] = gameLocal.time + 30000;
}
} else if ( chatType == IM_DISGUISED ) {
if ( playerInfo.lastChatTime[ IM_DISGUISED ] < gameLocal.time || forceChat ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/self/disguise/imindisguise" );
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ IM_DISGUISED ] = gameLocal.time + 30000;
}
} else if ( chatType == MINES_SPOTTED ) {
if ( playerInfo.lastChatTime[ MINES_SPOTTED ] < gameLocal.time || forceChat ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/enemy/minesspotted" );
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ MINES_SPOTTED ] = gameLocal.time + 45000;
}
} else if ( chatType == HOLD_VEHICLE ) {
if ( playerInfo.lastChatTime[ HOLD_VEHICLE ] < gameLocal.time || forceChat ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/vehicles/holdvehicle" );
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ HOLD_VEHICLE ] = gameLocal.time + 30000;
}
} else if ( chatType == ENEMY_DISGUISED_AS_ME ) {
if ( playerInfo.lastChatTime[ ENEMY_DISGUISED_AS_ME ] < gameLocal.time || forceChat ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/self/disguise/enemydisguisedasme" );
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ ENEMY_DISGUISED_AS_ME ] = gameLocal.time + 30000;
for( int i = 0; i < MAX_CLIENTS; i++ ) {
if ( GetGameWorldState()->clientInfo[ i ].team == playerInfo.team ) {
continue;
}
if ( GetGameWorldState()->clientInfo[ i ].classType != COVERTOPS ) {
continue;
}
if ( !GetGameWorldState()->clientInfo[ i ].isDisguised ) {
continue;
}
if ( GetGameWorldState()->clientInfo[ i ].disguisedClient != clientNum ) {
continue;
}
GetGameWorldState()->clientInfo[ i ].covertWarningTime = gameLocal.time;
break;
}
}
} else if ( chatType == ENDGAME_WIN && !bot_noTaunt.GetBool() ) {
if ( playerInfo.lastChatTime[ ENDGAME_WIN ] < gameLocal.time || forceChat ) {
int n = random.RandomInt( 11 );
if ( n == 0 ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/gdf" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/strogg" );
}
} else if ( n == 1 ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/killalienscum" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/killhumanfood" );
}
} else if ( n == 2 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/ohdear" );
} else if ( n == 3 ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/enemyweakened" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/theycrumble" );
}
} else if ( n == 4 ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/theyrunningaway" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/theyfleeterror" );
}
} else if ( n == 5 ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/freedomofearth" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/forthemakron" );
}
} else if ( n == 6 ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/eatthatstrogg" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/eatthathuman" );
}
} else if ( n == 7 ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/denied" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/thwarted" );
}
} else if ( n == 8 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/owned" );
} else if ( n == 9 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/goodgame" );
} else {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/goodgame" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/excellent" );
}
}
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ ENDGAME_WIN ] = gameLocal.time + 15000;
}
} else if ( chatType == ENDGAME_LOSE && !bot_noTaunt.GetBool() ) {
if ( playerInfo.lastChatTime[ ENDGAME_LOSE ] < gameLocal.time || forceChat ) {
int n = random.RandomInt( 13 );
if ( n == 0 ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/gdf" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/strogg" );
}
} else if ( n == 1 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/awkward" );
} else if ( n == 2 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/ohdear" );
} else if ( n == 3 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/oops" );
} else if ( n == 4 ) {
if ( playerInfo.team == GDF ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/grr" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/rrr" );
}
} else if ( n == 5 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/cough" );
} else if ( n == 6 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/thathurt" );
} else if ( n == 7 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/meh" );
} else if ( n == 8 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/awyeahohno" );
} else if ( n == 9 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/taunts/ihadworse" );
} else if ( n == 10 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/quiet" );
} else if ( n == 11 ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/cheers/goodgame" );
} else {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/quiet" );
}
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ ENDGAME_LOSE ] = gameLocal.time + 15000;
}
} else if ( chatType == SORRY ) {
if ( playerInfo.lastChatTime[ SORRY ] < gameLocal.time || forceChat ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/responses/sorry" );
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ SORRY ] = gameLocal.time + 15000;
}
} else if ( chatType == GLOBAL_SORRY ) {
if ( playerInfo.lastChatTime[ GLOBAL_SORRY ] < gameLocal.time || forceChat ) {
quickChatName = pc->BuildQuickChatDeclName( "quickchat/global/sorry" );
quickChat = gameLocal.declQuickChatType.LocalFind( quickChatName, false );
playerInfo.lastChatTime[ GLOBAL_SORRY ] = gameLocal.time + 5000;
}
}
if ( quickChat == NULL ) {
return;
}
gameLocal.ServerSendQuickChatMessage( player, quickChat, NULL, NULL );
playerInfo.chatDelay = gameLocal.time + 1000;
}
/*
================
idBotThreadData::NumClassOnTeam
Looks for all clients that are of class "classType" on team "playerTeam" and returns the number of clients.
================
*/
int idBotThreadData::GetNumClassOnTeam( const playerTeamTypes_t playerTeam, const playerClassTypes_t classType ) {
int n = 0;
if( !GetBotWorldState() ) {
return 0;
}
for( int i = 0; i < MAX_CLIENTS; i++ ) {
if ( !GetBotWorldState()->clientInfo[ i ].inGame ) {
continue;
}
if ( GetBotWorldState()->clientInfo[ i ].team != playerTeam ) {
continue;
}
if ( GetBotWorldState()->clientInfo[ i ].classType != NOCLASS ) {
if ( GetBotWorldState()->clientInfo[ i ].classType != classType ) {
continue;
}
} else {
if ( GetBotWorldState()->clientInfo[ i ].cachedClassType != classType ) {
continue;
}
}
n++;
}
return n;
}
/*
================
idBotThreadData::GetNumBotsInServer
Looks for the number of bots on the server. If playerTeam != NOTEAM, then only bots
playing on playerTeam.
================
*/
int idBotThreadData::GetNumBotsInServer( const playerTeamTypes_t playerTeam ) {
int n = 0;
int i;
for( i = 0; i < MAX_CLIENTS; i++ ) {
if ( !GetGameWorldState()->clientInfo[ i ].inGame ) {
continue;
}
if ( GetGameWorldState()->clientInfo[ i ].team == NOTEAM ) {
continue;
}
if ( playerTeam != NOTEAM ) {
if ( GetGameWorldState()->clientInfo[ i ].team != playerTeam ) {
continue;
}
}
if ( !GetGameWorldState()->clientInfo[ i ].isBot ) {
continue;
}
n++;
}
return n;
}
/*
================
idBotThreadData::TeamHasHumans
Does this team have any humans on it?
================
*/
bool idBotThreadData::TeamHasHumans( const playerTeamTypes_t playerTeam ) {
bool hasHumans = false;
int i;
for( i = 0; i < MAX_CLIENTS; i++ ) {
if ( !GetGameWorldState()->clientInfo[ i ].inGame ) {
continue;
}
if ( GetGameWorldState()->clientInfo[ i ].team == NOTEAM ) {
continue;
}
if ( GetGameWorldState()->clientInfo[ i ].isBot == true ) {
continue;
}
if ( GetGameWorldState()->clientInfo[ i ].team != playerTeam ) {
continue;
}
hasHumans = true;
break;
}
return hasHumans;
}
/*
================
idBotThreadData::ResetBotAI
Do a reset of the bot's AI.
================
*/
void idBotThreadData::ResetBotAI( const playerTeamTypes_t playerTeam ) {
int i;
for( i = 0; i < MAX_CLIENTS; i++ ) {
if ( !GetGameWorldState()->clientInfo[ i ].inGame ) {
continue;
}
if ( GetGameWorldState()->clientInfo[ i ].team == NOTEAM ) {
continue;
}
if ( GetGameWorldState()->clientInfo[ i ].isBot == false ) {
continue;
}
if ( GetGameWorldState()->clientInfo[ i ].team != playerTeam ) {
continue;
}
GetGameWorldState()->clientInfo[ i ].resetState = MAJOR_RESET_EVENT; //mal: give it total priority!
}
}
/*
================
idBotThreadData::UpdateChats;
If a human chats, see what they chatted, and if its something the bot's should worry about.
================
*/
void idBotThreadData::UpdateChats( int clientNum, const sdDeclQuickChat* quickChat ) {
int i;
if ( quickChat->GetType() == THANKS ) {
botThreadData.GetGameWorldState()->clientInfo[ clientNum ].lastThanksTime = gameLocal.time;
} else if ( quickChat->GetType() == ENEMY_DISGUISED_AS_ME ) {
for( i = 0; i < MAX_CLIENTS; i++ ) {
if ( botThreadData.GetGameWorldState()->clientInfo[ i ].team == botThreadData.GetGameWorldState()->clientInfo[ clientNum ].team ) {
continue;
}
if ( botThreadData.GetGameWorldState()->clientInfo[ i ].classType != COVERTOPS ) {
continue;
}
if ( !botThreadData.GetGameWorldState()->clientInfo[ i ].isDisguised ) {
continue;
}
if ( botThreadData.GetGameWorldState()->clientInfo[ i ].disguisedClient != clientNum ) {
continue;
}
botThreadData.GetGameWorldState()->clientInfo[ i ].covertWarningTime = gameLocal.time;
break;
}
}
botThreadData.GetGameWorldState()->clientInfo[ clientNum ].lastChatTime[ quickChat->GetType() ] = gameLocal.time;
}
/*
================
idBotThreadData::FindCurrentWeaponInVehicle
Returns what kind of weapon "player" has available to it in the vehicle position its in.
================
*/
void idBotThreadData::FindCurrentWeaponInVehicle( idPlayer *player, sdTransport* transport ) {
int weapIndex;
if ( transport->GetPositionManager().PositionForPlayer( player ).FindDefaultWeapon() == -1 ) {
if ( transport->GetPositionManager().PositionForPlayer( player ).GetAllowWeapon() ) {
botThreadData.GetGameWorldState()->clientInfo[ player->entityNumber ].proxyInfo.weapon = PERSONAL_WEAPON; //mal: we can use our main gun, pistols, and nades.
} else {
botThreadData.GetGameWorldState()->clientInfo[ player->entityNumber ].proxyInfo.weapon = NULL_VEHICLE_WEAPON;
}
return;
}
weapIndex = transport->GetPositionManager().PositionForPlayer( player ).GetWeaponIndex();
if( weapIndex == -1 ) {
botThreadData.GetGameWorldState()->clientInfo[ player->entityNumber ].proxyInfo.weapon = NULL_VEHICLE_WEAPON;
return;
}
switch( transport->GetVehicleType() ) {
case BADGER:
if ( weapIndex == 0 ) {
botThreadData.GetGameWorldState()->clientInfo[ player->entityNumber ].proxyInfo.weapon = MINIGUN;
}
break;
case TITAN:
if ( weapIndex == 0 ) {
botThreadData.GetGameWorldState()->clientInfo[ player->entityNumber ].proxyInfo.weapon = TANK_GUN;
} else if ( weapIndex == 1 ) {
botThreadData.GetGameWorldState()->clientInfo[ player->entityNumber ].proxyInfo.weapon = MINIGUN;
}
break;
case HOG:
if ( weapIndex == 0 ) {
botThreadData.GetGameWorldState()->clientInfo[ player->entityNumber ].proxyInfo.weapon = MINIGUN;
}
break;
case GOLIATH:
botThreadData.GetGameWorldState()->clientInfo[ player->entityNumber ].proxyInfo.weapon = TANK_GUN;
break;
case DESECRATOR:
if ( weapIndex == 0 ) {
botThreadData.GetGameWorldState()->clientInfo[ player->entityNumber ].proxyInfo.weapon = TANK_GUN;
} else if ( weapIndex == 1 ) {
botThreadData.GetGameWorldState()->clientInfo[ player->entityNumber ].proxyInfo.weapon = MINIGUN;
}
break;
case MCP:
case BUFFALO:
if ( weapIndex >= 0 ) {
botThreadData.GetGameWorldState()->clientInfo[ player->entityNumber ].proxyInfo.weapon = MINIGUN;
}
break;
case ICARUS:
botThreadData.GetGameWorldState()->clientInfo[ player->entityNumber ].proxyInfo.weapon = STROY_BOMB;
break;
case PLATYPUS:
if ( weapIndex == 0 ) {
botThreadData.GetGameWorldState()->clientInfo[ player->entityNumber ].proxyInfo.weapon = MINIGUN;
}
break;
case TROJAN:
if ( weapIndex == 0 ) {
botThreadData.GetGameWorldState()->clientInfo[ player->entityNumber ].proxyInfo.weapon = MINIGUN;
} else if ( weapIndex == 1 ) {
botThreadData.GetGameWorldState()->clientInfo[ player->entityNumber ].proxyInfo.weapon = LAW;
}
break;
case ANANSI:
case HORNET:
if ( weapIndex == 0 ) {
botThreadData.GetGameWorldState()->clientInfo[ player->entityNumber ].proxyInfo.weapon = LAW;
} else if ( weapIndex == 1 ) {
botThreadData.GetGameWorldState()->clientInfo[ player->entityNumber ].proxyInfo.weapon = ROCKETS;
} else if ( weapIndex == 2 ) {
botThreadData.GetGameWorldState()->clientInfo[ player->entityNumber ].proxyInfo.weapon = MINIGUN;
}
break;
}
// common->Printf("WeapIndex = %i\n", weapIndex );
// common->Printf("Weapon Ready = %i\n", botThreadData.GetGameWorldState()->clientInfo[ player->entityNumber ].proxyInfo.weaponIsReady );
}
/*
================
idBotThreadData::AddActionToHash
================
*/
void idBotThreadData::AddRouteToHash( const char *routeName, int routeNum ) {
if ( FindRouteByName( routeName ) != -1 ) {
gameLocal.Error( "Multiple bot routes named '%s'", routeName );
}
routeHash.Add( routeHash.GenerateKey( routeName, true ), routeNum );
}
/*
=============
idBotThreadData::FindRouteByName
Returns the route whose name matches the specified string.
=============
*/
int idBotThreadData::FindRouteByName( const char *routeName ) {
int hash, i;
hash = routeHash.GenerateKey( routeName, true );
for ( i = routeHash.GetFirst( hash ); i != -1; i = routeHash.GetNext( i ) ) {
if ( botRoutes[ i ]->name.Icmp( routeName ) == 0 ) {
return i;
}
}
return -1;
}
/*
=============
idBotThreadData::CheckCrossHairInfo
Give the bots a break, and let them easily use things in the world.
=============
*/
void idBotThreadData::CheckCrossHairInfo( idEntity *bot, sdCrosshairInfo &botCrossHairInfo ) {
int i;
float entDist = 350.0f;
idBounds entBounds;
idBox playerBox, entBox;
idVec3 vec;
playerBox = idBox( bot->GetPhysics()->GetBounds(), bot->GetPhysics()->GetOrigin(), bot->GetPhysics()->GetAxis() );
idEntity* botCrossHairEntity = botCrossHairInfo.GetEntity();
if ( botCrossHairEntity != NULL ) {
if ( botCrossHairEntity->IsType( sdTransport::Type ) ) {
entDist = 750.0f;
}
vec = botCrossHairEntity->GetPhysics()->GetOrigin() - bot->GetPhysics()->GetOrigin();
if ( vec.LengthSqr() < Square( entDist ) ) {
botCrossHairInfo.SetStartTime( gameLocal.time );
botCrossHairInfo.SetDistance( BOT_CROSSHAIR_DIST );
botCrossHairInfo.Validate();
}
}
if ( botThreadData.GetGameOutputState()->botOutput[ bot->entityNumber ].actionEntityNum != -1 ) {
idEntity* actionEnt = gameLocal.entities[ botThreadData.GetGameOutputState()->botOutput[ bot->entityNumber ].actionEntityNum ];
if ( actionEnt != NULL && gameLocal.GetSpawnId( actionEnt ) == botThreadData.GetGameOutputState()->botOutput[ bot->entityNumber ].actionEntitySpawnID ) { //mal: our actionEnt will override our crosshair entity, in the off chance they dont match.
if ( actionEnt != botCrossHairEntity ) {
entBounds = actionEnt->GetPhysics()->GetBounds( 0 );
for( i = 1; i < actionEnt->GetPhysics()->GetNumClipModels(); i++ ) { //mal: if the entity has multiple bounding boxs, add them all together for the overall bounds.
entBounds.AddBounds( actionEnt->GetPhysics()->GetBounds( i ) );
}
entBox = idBox( entBounds, actionEnt->GetPhysics()->GetOrigin(), actionEnt->GetPhysics()->GetAxis() );
entBox.ExpandSelf( VEHICLE_BOX_EXPAND );
if ( playerBox.IntersectsBox( entBox ) ) {
botCrossHairInfo.SetEntity( actionEnt );
botCrossHairInfo.SetStartTime( gameLocal.time );
botCrossHairInfo.SetDistance( BOT_CROSSHAIR_DIST );
botCrossHairInfo.Validate();
}
}
}
}
//mal: flag this bot as having a crosshair hint of some kind.
if ( botCrossHairInfo.IsUseValid() ) {
botThreadData.GetGameWorldState()->clientInfo[ bot->entityNumber ].hasCrosshairHint = true;
} else {
botThreadData.GetGameWorldState()->clientInfo[ bot->entityNumber ].hasCrosshairHint = false;
}
}
/*
===============
idBotThreadData::Nav_GetAreaNum
===============
*/
int idBotThreadData::Nav_GetAreaNum( int aasType, const idVec3 &origin ) {
idAAS *aas = GetAAS( aasType );
if ( aas == NULL ) {
return 0;
}
return aas->PointReachableAreaNum( origin, aas->GetSettings()->boundingBox, AAS_AREA_REACHABLE_WALK, 0 );
}
/*
===============
idBotThreadData::Nav_GetAreaNumAndOrigin
===============
*/
int idBotThreadData::Nav_GetAreaNumAndOrigin( int aasType, const idVec3& origin, idVec3& aasOrigin ) {
idAAS *aas = GetAAS( aasType );
if ( aas == NULL ) {
return 0;
}
int areaNum = aas->PointReachableAreaNum( origin, aas->GetSettings()->boundingBox, AAS_AREA_REACHABLE_WALK, 0 );
if ( areaNum > 0 ) {
aas->PushPointIntoArea( areaNum, aasOrigin );
}
return areaNum;
}
/*
===============
idBotThreadData::Nav_IsDirectPath
===============
*/
bool idBotThreadData::Nav_IsDirectPath( int aasType, const playerTeamTypes_t& playerTeam, int areaNum, const idVec3& origin, const idVec3& end ) {
idAAS *aas = GetAAS( aasType );
if ( aas == NULL ) {
return false;
}
if ( botThreadData.AllowDebugData() ) {
gameRenderWorld->DebugLine( colorGreen, origin, end, 16 );
}
int travelFlags = ( playerTeam == GDF ) ? TFL_VALID_GDF : TFL_VALID_STROGG;
if ( areaNum == NULL_AREANUM ) {
areaNum = aas->PointReachableAreaNum( origin, aas->GetSettings()->boundingBox, AAS_AREA_REACHABLE_WALK, 0 );
}
aasTraceFloor_t trace;
travelFlags &= ~TFL_WALKOFFLEDGE;
if ( aasType == AAS_VEHICLE ) {
travelFlags &= ~TFL_BARRIERJUMP;
}
aas->TraceFloor( trace, origin, areaNum, end, travelFlags );
return trace.fraction == 1.0f;
}
/*
===============
idBotThreadData::LoadBotNames
===============
*/
void idBotThreadData::LoadBotNames() {
botNames.Clear();
idLexer src( LEXFL_NOSTRINGESCAPECHARS | LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT );
idStr botFileName = "bots/";
botFileName += "botnames";
botFileName.SetFileExtension( "dat" );
src.LoadFile( botFileName );
if ( !src.IsLoaded() ) {
return;
}
idToken token;
while( true ) {
if ( !src.ReadToken( &token ) ) {
break;
}
if ( token.Length() > sdNetSession::MAX_NICKLEN ) {
gameLocal.Warning("Skipping invalid name found in botnames.dat! The name ^7\"%s^7\" ^1is too long! Max name length is %i characters!", token.c_str(), sdNetSession::MAX_NICKLEN );
continue;
}
botNames.Append( token.c_str() );
}
}
/*
===============
idBotThreadData::LoadTrainingBotNames
===============
*/
void idBotThreadData::LoadTrainingBotNames() {
bool inGDF = true;
trainingGDFBotNames.Clear();
trainingSTROGGBotNames.Clear();
idLexer src( LEXFL_NOSTRINGESCAPECHARS | LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT );
idStr botFileName = "bots/";
botFileName += "training_botnames";
botFileName.SetFileExtension( "dat" );
src.LoadFile( botFileName );
if ( !src.IsLoaded() ) {
return;
}
idToken token;
while( true ) {
if ( !src.ReadToken( &token ) ) {
break;
}
if ( token.Icmp( "[GDF]" ) == 0 ) {
inGDF = true;
continue;
} else if ( token.Icmp( "[STROGG]" ) == 0 ) {
inGDF = false;
continue;
}
if ( token.Length() > sdNetSession::MAX_NICKLEN ) {
gameLocal.Warning("Skipping invalid name found in training_botnames.dat! The name ^7\"%s^7\" ^1is too long! Max name length is %i characters!", token.c_str(), sdNetSession::MAX_NICKLEN );
continue;
}
if ( inGDF ) {
trainingGDFBotNames.Append( token.c_str() );
} else {
trainingSTROGGBotNames.Append( token.c_str() );
}
}
}
/*
================
idBotThreadData::FindDeclIndexForDeployable
================
*/
int idBotThreadData::FindDeclIndexForDeployable( const playerTeamTypes_t team, int deployableNum ) {
const char* name;
int index;
const idDecl* decl;
if ( team < GDF || team > STROGG ) {
gameLocal.DWarning( "Invalid team passed to \"FindDeclIndexForDeployable\"!\n" );
return -1;
}
if ( deployableNum == ARTILLERY ) {
if ( team == STROGG ) {
name = "deployobject_railhowitzer";
} else {
name = "deployobject_artillery";
}
} else if ( deployableNum == ROCKET_ARTILLERY ) {
if ( team == STROGG ) {
name = "deployobject_plasmamortar";
} else {
name = "deployobject_rockets";
}
} else if ( deployableNum == NUKE ) {
if ( team == STROGG ) {
name = "deployobject_ssg";
} else {
name = "deployobject_ssm";
}
} else if ( deployableNum == APT ) {
if ( team == STROGG ) {
name = "deployobject_antipersonnel_strogg";
} else {
name = "deployobject_antipersonnel_gdf";
}
} else if ( deployableNum == AVT ) {
if ( team == STROGG ) {
name = "deployobject_antiarmour_strogg";
} else {
name = "deployobject_antiarmour_gdf";
}
} else if ( deployableNum == RADAR ) {
if ( team == STROGG ) {
name = "deployobject_psi";
} else {
name = "deployobject_radar";
}
} else if ( deployableNum == AIT ) {
if ( team == STROGG ) {
name = "deployobject_shield_generator";
} else {
name = "deployobject_amt_gdf";
}
} else {
gameLocal.DWarning( "Invalid deployable type passed to \"FindDeclIndexForDeployable\"!\n" );
return -1;
}
decl = gameLocal.declDeployableObjectType[ name ];
index = ( decl ) ? decl->Index() : -1;
return index;
}
/*
================
idBotThreadData::RequestDeployableAtLocation
================
*/
bool idBotThreadData::RequestDeployableAtLocation( int clientNum, bool& needPause ) {
if ( !botThreadData.ClientIsValid( clientNum ) ) {
return false;
}
bool success;
int actionNumber = GetGameOutputState()->botOutput[ clientNum ].deployInfo.actionNumber;
idVec3 deployPoint = GetGameOutputState()->botOutput[ clientNum ].deployInfo.location;
idVec3 aimPoint = GetGameOutputState()->botOutput[ clientNum ].deployInfo.aimPoint;
idVec3 vec;
needPause = false;
idPlayer* player = gameLocal.GetClient( clientNum );
if ( player == NULL ) {
gameLocal.DWarning( "Invalid player trying to request a deployable!" );
return false;
}
int index = FindDeclIndexForDeployable( GetGameWorldState()->clientInfo[ clientNum ].team, GetGameOutputState()->botOutput[ clientNum ].deployInfo.deployableType );
if ( index == -1 ) {
gameLocal.DWarning( "Can't find deployable type %i in \"RequestDeployableAtLocation\"! ", GetGameOutputState()->botOutput[ clientNum ].deployInfo.deployableType );
return false;
}
const sdDeclDeployableObject* object = gameLocal.declDeployableObjectType.SafeIndex( index );
if ( object == NULL ) {
gameLocal.DWarning( "Client %i tried to drop a deployable that doesn't exist!", clientNum );
return false;
}
if ( player->CheckDeployPosition( deployPoint, object ) != DR_CLEAR ) { //mal: oops - hint origin is not on target.
gameLocal.DWarning( "Client %i can't deploy at action number %i!", clientNum, actionNumber );
return false;
}
if ( botThreadData.DestroyClientDeployable( clientNum ) ) {
needPause = true;
return false;
}
if ( aimPoint == vec3_zero ) {
vec = player->GetPhysics()->GetOrigin() - deployPoint;
} else {
vec = aimPoint - deployPoint;
}
float rotation = vec.ToAngles()[ YAW ];
success = gameLocal.RequestDeployment( player, object, deployPoint, rotation, 0 );
if ( success ) {
GetGameWorldState()->clientInfo[ clientNum ].deployDelayTime = gameLocal.time + 60000; //mal: dont try this again for a while.
gameLocal.SetTargetTimer( GetGameWorldState()->clientInfo[ clientNum ].scriptHandler.deployTimer, player, gameLocal.time + 60000 );
} else {
return false;
}
return true;
}
/*
================
idBotThreadData::CheckTargetLockInfo
================
*/
void idBotThreadData::CheckTargetLockInfo( int clientNum, float& boundsScale ) {
int i;
proxyInfo_t vehicleInfo;
if ( GetBotAimSkill() < 1 || GetBotSkill() < BOT_SKILL_NORMAL ) {
return;
}
if ( GetGameWorldState()->clientInfo[ clientNum ].proxyInfo.entNum == CLIENT_HAS_NO_VEHICLE ) {
boundsScale = 2.0f;
return;
}
vehicleInfo.entNum = 0;
for( i = 0; i < MAX_VEHICLES; i++ ) {
if ( GetGameWorldState()->vehicleInfo[ i ].entNum != GetGameWorldState()->clientInfo[ clientNum ].proxyInfo.entNum ) {
continue;
}
vehicleInfo = GetGameWorldState()->vehicleInfo[ i ];
break;
}
if ( vehicleInfo.entNum == 0 ) {
return;
}
if ( vehicleInfo.type == ANANSI ) {
boundsScale = 4.0f;
} else {
boundsScale = 2.0f;
}
}
/*
================
idBotThreadData::GetNumClientsOnTeam
Looks for all clients that are on team "playerTeam" and returns the number of clients.
================
*/
int idBotThreadData::GetNumClientsOnTeam( const playerTeamTypes_t playerTeam ) {
int n = 0;
if( !GetBotWorldState() ) {
return 0;
}
for( int i = 0; i < MAX_CLIENTS; i++ ) {
if ( !GetBotWorldState()->clientInfo[ i ].inGame ) {
continue;
}
if ( GetBotWorldState()->clientInfo[ i ].team == NOTEAM ) {
continue;
}
if ( GetBotWorldState()->clientInfo[ i ].team != playerTeam ) {
continue;
}
n++;
}
return n;
}
/*
================
idBotThreadData::GetNumTeamBotsSpawningAtRearBase
================
*/
int idBotThreadData::GetNumTeamBotsSpawningAtRearBase( const playerTeamTypes_t playerTeam ) {
int n = 0;
for( int i = 0; i < MAX_CLIENTS; i++ ) {
if ( !GetGameWorldState()->clientInfo[ i ].inGame ) {
continue;
}
if ( !GetGameWorldState()->clientInfo[ i ].isBot ) {
continue;
}
if ( GetGameWorldState()->clientInfo[ i ].team != playerTeam ) {
continue;
}
if ( GetGameWorldState()->clientInfo[ i ].wantsVehicle == false ) {
continue;
}
n++;
}
return n;
}
/*
================
idBotThreadData::GetNumWeaponsOnTeam
Looks for all clients that are on team "playerTeam" and returns how many of them have "weapType".
================
*/
int idBotThreadData::GetNumWeaponsOnTeam( const playerTeamTypes_t playerTeam, const playerWeaponTypes_t weapType ) {
int n = 0;
for( int i = 0; i < MAX_CLIENTS; i++ ) {
if ( !GetGameWorldState()->clientInfo[ i ].inGame ) {
continue;
}
if ( GetGameWorldState()->clientInfo[ i ].team != playerTeam ) {
continue;
}
if ( GetGameWorldState()->clientInfo[ i ].weapInfo.primaryWeapon != weapType ) {
continue;
}
n++;
}
return n;
}
/*
================
idBotThreadData::GetNumBotsOnTeam
Looks for all bots that are on team "playerTeam" and returns the number of bots.
================
*/
int idBotThreadData::GetNumBotsOnTeam( const playerTeamTypes_t playerTeam ) {
int n = 0;
if( !GetBotWorldState() ) {
return 0;
}
for( int i = 0; i < MAX_CLIENTS; i++ ) {
if ( !GetBotWorldState()->clientInfo[ i ].isBot ) {
continue;
}
if ( !GetBotWorldState()->clientInfo[ i ].inGame ) {
continue;
}
if ( GetBotWorldState()->clientInfo[ i ].team != playerTeam ) {
continue;
}
n++;
}
return n;
}
/*
================
idBotThreadData::GetBotDebugInfo
Send the bot's debug data to the game.
================
*/
botDebugInfo_t idBotThreadData::GetBotDebugInfo( int clientNum ) {
if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) {
botDebugInfo_t botInfo;
memset( &botInfo, 0, sizeof( botInfo ) );
return botInfo;
}
idPlayer *player = gameLocal.GetClient( clientNum );
botDebugInfo_t botInfo;
memset( &botInfo, 0, sizeof( botInfo ) );
if ( player != NULL && bot_debug.GetBool() ) {
if ( GetGameWorldState()->clientInfo[ clientNum ].isBot ) {
botInfo = botThreadData.GetGameOutputState()->botOutput[ clientNum ].debugInfo;
botInfo.inUse = true;
}
}
if ( botInfo.inUse ) {
bot_showPath.SetInteger( clientNum );
// aas_showObstacleAvoidance.SetInteger( 1 );
}
return botInfo;
}
/*
================
idBotThreadData::ChangeBotName
================
*/
void idBotThreadData::ChangeBotName( idPlayer* bot ) {
idStr playerName;
if ( !FindRandomBotName( bot->entityNumber, playerName ) ) {
const char * basePlayerName = "Bot";
if ( GetGameWorldState()->clientInfo[ bot->entityNumber ].team == GDF ) {
basePlayerName = "GDF Bot";
} else {
basePlayerName = "Strogg Bot";
}
sprintf( playerName, "%s %d", basePlayerName, bot->entityNumber );
}
networkSystem->ServerSetBotUserName( bot->entityNumber, playerName );
}
/*
============
idBotThreadData::FindRandomBotName
Make sure we dont have a duplicate name from a human player, or another bot.
Randomize the name selection.
============
*/
bool idBotThreadData::FindRandomBotName( int clientNum, idStr& botName ) {
if ( botThreadData.botNames.Num() == 0 ) {
return false;
}
if ( botThreadData.GetGameWorldState()->botGoalInfo.isTrainingMap ) {
botName = "";//"Player";
return true;
}
bool foundName = false;
int i;
int k;
idStrList tempBotNames = botThreadData.botNames;
while( tempBotNames.Num() > 0 ) {
k = botThreadData.random.RandomInt( tempBotNames.Num() );
botName = tempBotNames[ k ];
gameLocal.CleanName( botName );
for ( i = 0; i < MAX_CLIENTS; i++ ) {
if ( i == clientNum ) {
continue;
}
idPlayer* player = gameLocal.GetClient( i );
if ( !player ) {
continue;
}
if ( !idStr::Icmp( botName, player->userInfo.cleanName ) ) {
break;
}
}
if ( i == MAX_CLIENTS ) {
botName = tempBotNames[ k ];
foundName = true;
break;
}
tempBotNames.RemoveIndexFast( k );
}
return foundName;
}
/*
============
idBotThreadData::FindActionByTypeForLocation
============
*/
int idBotThreadData::FindActionByTypeForLocation( const idVec3& loc, const botActionGoals_t& obj, const playerTeamTypes_t& team ) {
int actionNumber = ACTION_NULL;
for( int i = 0; i < botThreadData.botActions.Num(); i++ ) {
if ( !botThreadData.botActions[ i ]->ActionIsActive() ) {
continue;
}
if ( !botThreadData.botActions[ i ]->ActionIsValid() ) {
continue;
}
if ( team == GDF ) {
if ( botThreadData.botActions[ i ]->GetHumanObj() != obj ) {
continue;
}
} else {
if ( botThreadData.botActions[ i ]->GetStroggObj() != obj ) {
continue;
}
}
if ( !botThreadData.botActions[ i ]->actionBBox.Expand( ACTION_BBOX_EXPAND_BIG ).ContainsPoint( loc ) ) {
continue;
}
actionNumber = i;
break;
}
return actionNumber;
}
/*
============
idBotThreadData::CheckCurrentChatRequests
============
*/
void idBotThreadData::CheckCurrentChatRequests() {
if ( nextChatUpdateTime > gameLocal.time ) {
return;
}
nextChatUpdateTime = gameLocal.time + 1000;
for( int i = 0; i < MAX_CLIENTS; i++ ) {
clientInfo_t& player = GetGameWorldState()->clientInfo[ i ];
if ( player.inGame == false || player.team == NOTEAM ) {
continue;
}
if ( player.isBot ) {
continue;
}
if ( player.repairTargetNum == -1 ) {
continue;
}
if ( player.repairTargetUpdateTime + MAX_TARGET_TIME > gameLocal.time || player.repairTargetUpdateTime == 0 ) {
continue;
}
if ( player.repairTargetNeedsChat == false ) {
continue;
}
for( int j = 0; j < MAX_CLIENTS; j++ ) { //mal: find a bot eng to respond in the negative.
clientInfo_t& bot = GetGameWorldState()->clientInfo[ j ];
if ( bot.inGame == false || bot.team == NOTEAM ) {
continue;
}
if ( !bot.isBot ) {
continue;
}
if ( bot.team != player.team ) {
continue;
}
if ( bot.classType != ENGINEER ) {
continue;
}
VOChat( CMD_DECLINED, j, true );
break;
}
player.repairTargetNeedsChat = false; //mal: whether we responded or not, dont check this client again.
break;
}
for( int i = 0; i < MAX_CLIENTS; i++ ) {
clientInfo_t& player = GetGameWorldState()->clientInfo[ i ];
if ( player.inGame == false || player.team == NOTEAM ) {
continue;
}
if ( player.isBot ) {
continue;
}
if ( player.killTargetNum == -1 ) {
continue;
}
if ( player.killTargetUpdateTime + MAX_TARGET_TIME > gameLocal.time || player.killTargetUpdateTime == 0 ) {
continue;
}
if ( player.killTargetNeedsChat == false ) {
continue;
}
for( int j = 0; j < MAX_CLIENTS; j++ ) { //mal: find a bot to respond in the negative.
clientInfo_t& bot = GetGameWorldState()->clientInfo[ j ];
if ( bot.inGame == false || bot.team == NOTEAM ) {
continue;
}
if ( !bot.isBot ) {
continue;
}
if ( bot.team != player.team ) {
continue;
}
VOChat( CMD_DECLINED, j, true );
break;
}
player.killTargetNeedsChat = false; //mal: dont check this client again.
break;
}
for( int i = 0; i < MAX_CLIENTS; i++ ) {
clientInfo_t& player = GetGameWorldState()->clientInfo[ i ];
if ( player.inGame == false || player.team == NOTEAM ) {
continue;
}
if ( player.isBot ) {
continue;
}
if ( player.commandRequestTime + MAX_TARGET_TIME > gameLocal.time || player.commandRequestTime == 0 ) {
continue;
}
if ( player.commandRequestChatSent == true ) {
continue;
}
for( int j = 0; j < MAX_CLIENTS; j++ ) { //mal: find a critical class bot to respond in the affirmative.
clientInfo_t& bot = GetGameWorldState()->clientInfo[ j ];
if ( bot.inGame == false || bot.team == NOTEAM ) {
continue;
}
if ( !bot.isBot ) {
continue;
}
if ( bot.team != player.team ) {
continue;
}
if ( player.team == GDF ) {
if ( bot.classType != GetGameWorldState()->botGoalInfo.team_GDF_criticalClass ) {
continue;
}
} else {
if ( bot.classType != GetGameWorldState()->botGoalInfo.team_STROGG_criticalClass ) {
continue;
}
}
if ( !GetGameWorldState()->gameLocalInfo.heroMode && botThreadData.GetBotSkill() != BOT_SKILL_DEMO ) {//mal: this is just for show anyhow, but if human has "Hero" mode on, the bot can't do the obj - so let the human know.
VOChat( ACKNOWLEDGE_YES, j, true );
} else {
VOChat( CMD_DECLINED, j, true );
}
break;
}
player.commandRequestChatSent = true; //mal: whether we responded or not, dont check this client again.
break;
}
}
/*
============
idBotThreadData::ResetBotsInfo
============
*/
void idBotThreadData::ResetBotsInfo() {
for( int i = 0; i < MAX_CLIENTS; i++ ) {
idPlayer *player = gameLocal.GetClient( i );
if ( player == NULL ) {
continue;
}
if ( !player->IsType( idBot::Type ) ) {
continue;
}
clientInfo_t& client = GetGameWorldState()->clientInfo[ i ];
client.isBot = true;
client.inGame = true;
idBotAI *botAI = new idBotAI;
botAI->ClearBotState();
botAI->ClearBotUcmd( i );
botAI->SetBotClientNum( i );
botAI->InitAAS();
botThreadData.bots[ i ] = botAI;
}
}
/*
============
idBotThreadData::SetupBotInfo
============
*/
void idBotThreadData::SetupBotInfo( int clientNum ) {
clientInfo_t& client = GetGameWorldState()->clientInfo[ clientNum ];
client.isBot = true;
client.inGame = true;
idBotAI *botAI = new idBotAI;
botAI->ClearBotState();
botAI->ClearBotUcmd( clientNum );
botAI->SetBotClientNum( clientNum );
botAI->InitAAS();
botThreadData.bots[ clientNum ] = botAI;
}
/*
============
idBotThreadData::UpdateClientAbilities
============
*/
void idBotThreadData::UpdateClientAbilities( int clientNum, int rankLevel ) {
if ( !botThreadData.ClientIsValid( clientNum ) ) {
return;
}
clientInfo_t& client = GetGameWorldState()->clientInfo[ clientNum ];
client.abilities.rank = rankLevel;
if ( client.abilities.rank == 1 ) {
client.abilities.fasterMedicCharge = true;
client.abilities.grenadeLauncher = true;
client.abilities.stroggCovertNoFootSteps = true;
} else if ( client.abilities.rank == 2 ) {
client.abilities.stroggAdrenaline = true;
client.abilities.stroggRepairDrone = true;
} else if ( client.abilities.rank == 3 ) {
client.abilities.gdfAdrenaline = true;
} else {
client.abilities.selfArmingMines = true;
client.abilities.gdfStealthToRadar = true;
}
}
/*
============
idBotThreadData::ClientIsValid
============
*/
bool idBotThreadData::ClientIsValid( int clientNum ) {
if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) {
if ( botThreadData.AllowDebugData() ) {
assert( false );
}
return false;
}
return true;
}
/*
============
idBotThreadData::ClearClientBoundEntities
============
*/
void idBotThreadData::ClearClientBoundEntities( int clientNum ) {
if ( !ClientIsValid( clientNum ) ) {
return;
}
for( int i = 0; i < MAX_DEPLOYABLES; i++ ) {
deployableInfo_t& deployable = GetGameWorldState()->deployableInfo[ i ];
if ( deployable.entNum == 0 ) {
continue;
}
if ( deployable.ownerClientNum != clientNum ) {
continue;
}
idEntity *deployableEnt = gameLocal.entities[ deployable.entNum ];
if ( deployableEnt == NULL ) {
continue;
}
deployableEnt->Damage( NULL, NULL, idVec3( 0.0f, 0.0f, 1.0f ), DAMAGE_FOR_NAME( "damage_deployable_destruct" ), 1.0f, NULL );
break;
}
clientInfo_t& client = GetGameWorldState()->clientInfo[ clientNum ];
for( int i = 0; i < MAX_MINES; i++ ) {
if ( client.weapInfo.landMines[ i ].entNum == 0 ) {
continue;
}
idEntity *landMine = gameLocal.entities[ client.weapInfo.landMines[ i ].entNum ];
if ( landMine == NULL ) {
continue;
}
landMine->ProcessEvent( &EV_Remove ); //mal: fizzle, don't explode.
}
if ( client.weapInfo.covertToolInfo.entNum != 0 ) {
idEntity *covertTool = gameLocal.entities[ client.weapInfo.covertToolInfo.entNum ];
if ( covertTool != NULL ) {
covertTool->Damage( NULL, NULL, idVec3( 0.0f, 0.0f, 1.0f ), DAMAGE_FOR_NAME( "damage_generic" ), 999.0f, NULL );
}
}
if ( client.supplyCrate.entNum != 0 ) {
idEntity *supplyCrate = gameLocal.entities[ client.supplyCrate.entNum ];
if ( supplyCrate != NULL ) {
supplyCrate->Damage( NULL, NULL, idVec3( 0.0f, 0.0f, 1.0f ), DAMAGE_FOR_NAME( "damage_grenade_frag_splash" ), 999.0f, NULL );
}
}
}
/*
============
idBotThreadData::CheckBotClassSpread
============
*/
void idBotThreadData::CheckBotClassSpread() {
if( !gameLocal.isServer ) {
return;
}
if ( !bot_balanceCriticalClass.GetBool() ) {
return;
}
if ( !bot_allowClassChanges.GetBool() ) {
return;
}
if ( gameLocal.GameState() != GAMESTATE_ACTIVE ) {
return;
}
if( !GetBotWorldState() ) {
return;
}
if ( !CheckIfClientsInGameYet() ) {
return;
}
if ( GetGameWorldState()->botGoalInfo.isTrainingMap ) {
return;
}
if ( GetNumBotsInServer( NOTEAM ) == 0 ) {
return;
}
if ( gameLocal.numClients < bot_minClients.GetInteger() ) { //mal: game is still in process of adding bots, so leave.
return;
}
//mal: disabling for now, if have time - will come back to later....
/*
for( int i = 0; i < MAX_CLIENTS; i++ ) { //mal: make sure any engs that have the nade launcher upgrade, switch to it.
clientInfo_t& playerInfo = GetGameWorldState()->clientInfo[ i ];
if ( !playerInfo.inGame ) {
continue;
}
if ( playerInfo.classType != ENGINEER ) {
continue;
}
if ( !playerInfo.isBot ) {
continue;
}
if ( playerInfo.health > 0 ) {
continue;
}
if ( playerInfo.weapInfo.primaryWeapon == SHOTGUN ) {
continue;
}
if ( !playerInfo.abilities.grenadeLauncher ) {
continue;
}
if ( ( playerInfo.lastWeapChangedTime + MIN_WEAPON_CHANGE_DELAY ) > gameLocal.time ) {
continue;
}
idPlayer* player = gameLocal.GetClient( i );
if ( player == NULL ) {
continue;
}
const sdDeclPlayerClass* pc;
if ( playerInfo.team == GDF ) {
pc = gameLocal.declPlayerClassType[ "engineer" ];
} else {
pc = gameLocal.declPlayerClassType[ "constructor" ];
}
player->ChangeClass( pc, 2 ); //mal: switch to the nade launcher.
playerInfo.lastWeapChangedTime = gameLocal.time;
playerInfo.weapInfo.hasNadeLauncher = true;
}
*/
if ( nextBotClassUpdateTime > gameLocal.time ) {
return;
}
ManageBotClassesOnSmallServer();
if ( !gameLocal.rules->IsWarmup() ) {
nextBotClassUpdateTime = gameLocal.time + 5000; //mal: this isn't super critical.
} else {
nextBotClassUpdateTime = gameLocal.time + 1000;
}
return;
}
/*
============
idBotThreadData::CheckBotSpawnLocation
============
*/
void idBotThreadData::CheckBotSpawnLocation( idPlayer* player ) {
if ( player == NULL ) {
return;
}
clientInfo_t& playerInfo = botThreadData.GetGameWorldState()->clientInfo[ player->entityNumber ];
playerInfo.wantsVehicle = false;
if ( playerInfo.team == STROGG ) {
idEntity* spawnHost = player->GetSpawnPoint();
if ( spawnHost != NULL && spawnHost->IsType( sdDynamicSpawnPoint::Type ) ) {
return;
} else {
player->SetSpawnPoint( NULL );
}
} else {
player->SetSpawnPoint( NULL );
}
if ( !bot_useRearSpawn.GetBool() ) {
return;
}
sdTeamInfo* team = player->GetGameTeam();
if ( team == NULL ) {
return;
}
const playerTeamTypes_t botTeam = team->GetBotTeam();
if ( botTeam == GDF && !botThreadData.GetGameWorldState()->gameLocalInfo.teamUsesSpawnInfo[ GDF ].teamUsesSpawn ) {
return;
}
if ( botTeam == STROGG && !botThreadData.GetGameWorldState()->gameLocalInfo.teamUsesSpawnInfo[ STROGG ].teamUsesSpawn ) {
return;
}
idEntity* homeBaseSpawn = team->GetHomeBaseSpawn();
if ( homeBaseSpawn != NULL && homeBaseSpawn->GetPhysics() ) {
bool useRearSpawn = false;
int percentage;
if ( botTeam == GDF ) {
if ( botThreadData.GetGameWorldState()->botGoalInfo.team_GDF_criticalClass != playerInfo.classType ) {
percentage = botThreadData.GetGameWorldState()->gameLocalInfo.teamUsesSpawnInfo[ GDF ].percentage;
useRearSpawn = true;
}
} else if ( botTeam == STROGG ) {
if ( botThreadData.GetGameWorldState()->botGoalInfo.team_STROGG_criticalClass != playerInfo.classType ) {
percentage = botThreadData.GetGameWorldState()->gameLocalInfo.teamUsesSpawnInfo[ STROGG ].percentage;
useRearSpawn = true;
}
}
if ( useRearSpawn ) {
idVec3 homeOrg = homeBaseSpawn->GetPhysics()->GetOrigin();
int vehicleNum = FindHeavyVehicleNearLocation( player, homeOrg, MAX_VEHICLE_RANGE ); //mal: make sure have worthwhile vehicles before spawn back there.
if ( vehicleNum == -1 ) {
useRearSpawn = false;
}
}
if ( useRearSpawn && gameLocal.random.RandomInt( 100 ) < percentage && GetNumTeamBotsSpawningAtRearBase( botTeam ) < MAX_REAR_SPAWN_NUMBER ) {
player->SetSpawnPoint( team->GetHomeBaseSpawn() );
playerInfo.wantsVehicle = true;
}
}
}
/*
==================
idBotThreadData::FindHeavyVehicleNearLocation
Expanded to make an intelligent choice as to whether or not the bot should spawn back to grab a vehicle. If no vehicle matches its current potential goals, wont spawn back.
==================
*/
int idBotThreadData::FindHeavyVehicleNearLocation( idPlayer* player, const idVec3& org, float range ) {
bool hasAirVehicleGoal = false;
bool hasArmorGoal = false;
int entNum = -1;
float closest = idMath::INFINITY;
sdTeamInfo* playerTeam = player->GetTeam();
if ( playerTeam == NULL ) {
assert( false );
return entNum;
}
for( int i = 0; i < botActions.Num(); i++ ) {
if ( !botThreadData.botActions[ i ]->ActionIsActive() ) {
continue;
}
if ( !botThreadData.botActions[ i ]->ActionIsValid() ) {
continue;
}
if ( botActions[ i ]->GetBaseObjForTeam( playerTeam->GetBotTeam() ) != ACTION_VEHICLE_CAMP && botActions[ i ]->GetBaseObjForTeam( playerTeam->GetBotTeam() ) != ACTION_VEHICLE_ROAM ) {
continue;
}
if ( botActions[ i ]->GetActionVehicleFlags( playerTeam->GetBotTeam() ) & AIR || botActions[ i ]->GetActionVehicleFlags( playerTeam->GetBotTeam() ) == NULL_VEHICLE_FLAGS ) {
hasAirVehicleGoal = true;
}
if ( botActions[ i ]->GetActionVehicleFlags( playerTeam->GetBotTeam() ) & ARMOR || botActions[ i ]->GetActionVehicleFlags( playerTeam->GetBotTeam() ) == NULL_VEHICLE_FLAGS ) {
hasArmorGoal = true;
}
if ( hasArmorGoal && hasAirVehicleGoal ) { //mal: only check for as long as need to.
break;
}
}
for( int i = 0; i < MAX_VEHICLES; i++ ) {
const proxyInfo_t &vehicle = GetGameWorldState()->vehicleInfo[ i ];
if ( vehicle.entNum == 0 ) {
continue;
}
if ( vehicle.team != playerTeam->GetBotTeam() ) {
continue;
}
if ( vehicle.inWater ) { //mal: someone drove it into the ocean.
continue;
}
if ( vehicle.isFlipped ) { //mal: its on its back.
continue;
}
if ( !vehicle.hasGroundContact && vehicle.type != DESECRATOR ) {
continue;
}
if ( !( vehicle.flags & ARMOR ) && vehicle.type <= ICARUS ) {
continue;
}
if ( !hasAirVehicleGoal && vehicle.type > ICARUS ) {
continue;
}
if ( !hasArmorGoal && vehicle.flags & ARMOR ) {
continue;
}
if ( vehicle.damagedPartsCount > 0 ) { //mal: dont get a vehicle that has a missing wheel,
continue;
}
if ( vehicle.isOwned && vehicle.ownerNum != player->entityNumber ) {
continue;
}
if ( !vehicle.isEmpty ) {
continue;
}
if ( !vehicle.inPlayZone ) {
continue;
}
idVec3 vec = vehicle.origin - org;
if ( vec.LengthSqr() > Square( range ) ) {
continue;
}
entNum = vehicle.entNum;
break;
}
return entNum;
}
/*
================
idBotThreadData::GetVehicleTargetOffset
TODO: add additional offsets for any other vehicles needed.
================
*/
float idBotThreadData::GetVehicleTargetOffset( const playerVehicleTypes_t vehicleType ) {
float offset = 0.80f;
if ( vehicleType == HOG ) {
offset = 0.50f; // was .60
} else if ( vehicleType == MCP ) {
offset = 0.40f;
} else if ( vehicleType == GOLIATH ) {
offset = 0.50f;
}
return offset;
}
/*
================
idBotThreadData::GetVehicleTestBounds
================
*/
const idClipModel* idBotThreadData::GetVehicleTestBounds( const playerVehicleTypes_t vehicleType ) {
if ( vehicleType == ICARUS || vehicleType == HUSKY ) {
return personalVehicleClipModel;
} else if ( vehicleType == GOLIATH ) {
return goliathVehicleClipModel;
} else {
return genericVehicleClipModel;
}
}
/*
================
idBotThreadData::GetDeployableActionNumber
================
*/
int idBotThreadData::GetDeployableActionNumber( idList< int >& deployableActions, const idVec3& deployableOrigin, const playerTeamTypes_t deployableTeam, int deployableType ) {
if ( deployableActions.Num() == 0 ) {
return ACTION_NULL;
}
int actionNumber = ACTION_NULL;
for( int i = 0; i < deployableActions.Num(); i++ ) {
if ( botActions[ deployableActions[ i ] ]->GetBaseObjForTeam( deployableTeam ) != ACTION_DROP_DEPLOYABLE && botActions[ deployableActions[ i ] ]->GetBaseObjForTeam( deployableTeam ) != ACTION_DROP_PRIORITY_DEPLOYABLE ) {
continue;
}
int actionNum = deployableActions[ i ];
if ( deployableType != -1 ) {
if ( !( deployableType & botActions[ actionNum ]->GetDeployableType() ) ) {
continue;
}
}
float radius = botActions[ actionNum ]->GetRadius();
idVec3 vec = botActions[ actionNum ]->GetActionOrigin() - deployableOrigin;
if ( vec.LengthSqr() > Square( botActions[ actionNum ]->GetRadius() ) ) {
continue;
}
actionNumber = actionNum;
break;
}
return actionNumber;
}
/*
================
idBotThreadData::OnPlayerKilled
================
*/
void idBotThreadData::OnPlayerKilled( int clientNum ) {
idPlayer* player = gameLocal.GetClient( clientNum );
if ( player == NULL ) {
return;
}
clientInfo_t& playerInfo = botThreadData.GetGameWorldState()->clientInfo[ clientNum ];
playerInfo.isCamper = false;
playerInfo.wantsVehicle = false;
playerInfo.killsSinceSpawn = 0;
playerInfo.killCounter = 0;
playerInfo.favoriteKill = -1;
memset( playerInfo.kills, -1, sizeof( playerInfo.kills ) );
}
/*
================
idBotThreadData::Cmd_KillAllBots_f
================
*/
void idBotThreadData::Cmd_KillAllBots_f( const idCmdArgs &args ) {
for( int i = 0; i < MAX_CLIENTS; i++ ) {
idPlayer* player = gameLocal.GetClient( i );
if ( player == NULL ) {
continue;
}
if ( !player->IsType( idBot::Type ) ) {
continue;
}
player->Kill( NULL );
}
}
/*
================
idBotThreadData::Cmd_ResetAllBots_f
================
*/
void idBotThreadData::Cmd_ResetAllBots_f( const idCmdArgs & args ) {
for( int i = 0; i < MAX_CLIENTS; i++ ) {
idPlayer* player = gameLocal.GetClient( i );
if ( player == NULL ) {
continue;
}
if ( !player->IsType( idBot::Type ) ) {
continue;
}
clientInfo_t& playerInfo = botThreadData.GetGameWorldState()->clientInfo[ i ];
playerInfo.resetState = MAJOR_RESET_EVENT;
botThreadData.bots[ i ]->ResetLastActionNum();
}
}
/*
================
idBotThreadData::ServerHasHumans
================
*/
bool idBotThreadData::ServerHasHumans() {
bool hasHumans = false;
for( int i = 0; i < MAX_CLIENTS; i++ ) {
idPlayer* player = gameLocal.GetClient( i );
if ( player == NULL ) {
continue;
}
if ( player->IsType( idBot::Type ) ) {
continue;
}
hasHumans = true;
break;
}
return hasHumans;
}
/*
================
idBotThreadData::ActionIsLinkedToAction
================
*/
bool idBotThreadData::ActionIsLinkedToAction( int curActionNum, int testActionNum ) {
if ( !CheckActionIsValid( curActionNum ) || !CheckActionIsValid( testActionNum ) ) {
return false;
}
if ( botActions[ curActionNum ]->GetActionGroup() != botActions[ testActionNum ]->GetActionGroup() ) {
return false;
}
if ( ( botActions[ curActionNum ]->GetHumanObj() != botActions[ testActionNum ]->GetHumanObj() ) && ( botActions[ curActionNum ]->GetStroggObj() != botActions[ testActionNum ]->GetStroggObj() ) ) {
return false;
}
return true;
}
/*
==================
idBotThreadData::CheckActionIsValid
==================
*/
bool idBotThreadData::CheckActionIsValid( int actionNumber ) {
if ( actionNumber <= ACTION_NULL || actionNumber > botActions.Num() ) {
assert( false );
return false;
}
return true;
}
/*
==================
idBotThreadData::DestroyClientDeployable
==================
*/
bool idBotThreadData::DestroyClientDeployable( int clientNum ) {
bool hasDeployable = false;
for( int i = 0; i < MAX_DEPLOYABLES; i++ ) {
if ( GetGameWorldState()->deployableInfo[ i ].entNum == 0 ) {
continue;
}
if ( GetGameWorldState()->deployableInfo[ i ].ownerClientNum != clientNum ) {
continue;
}
idEntity *deployable = gameLocal.entities[ GetGameWorldState()->deployableInfo[ i ].entNum ];
if ( deployable == NULL || gameLocal.GetSpawnId( deployable ) != GetGameWorldState()->deployableInfo[ i ].spawnID ) {
if ( bot_debug.GetBool() ) {
gameLocal.Warning( "Bot has a deployable in the world it can't find!" );
}
break;
}
deployable->Damage( NULL, NULL, idVec3( 0.0f, 0.0f, 1.0f ), DAMAGE_FOR_NAME( "damage_deployable_destruct" ), 1.0f, NULL );
hasDeployable = true;
break;
}
return hasDeployable;
}
/*
==================
idBotThreadData::DrawActionNumber
==================
*/
void idBotThreadData::DrawActionNumber( int actionNumber, const idMat3& viewAxis, bool drawAllInfo ) {
idVec3 end = botActions[ actionNumber ]->origin;
end.z += 64.0f;
gameRenderWorld->DebugLine( colorGreen, botActions[ actionNumber ]->origin, end, 16 );
end.z += 8.0f;
gameRenderWorld->DrawText( va( "Action: %i", actionNumber ), end, bot_drawActionSize.GetFloat(), colorWhite, viewAxis );
end.z -= 16.0f;
gameRenderWorld->DrawText( va( "Name: %s", botActions[ actionNumber ]->name.c_str() ), end, 0.2f, colorWhite, viewAxis );
end.z -= 16.0f;
gameRenderWorld->DrawText( va( "GDF Goal: %i Strogg Goal: %i", botActions[ actionNumber ]->GetHumanObj(), botActions[ actionNumber ]->GetStroggObj() ), end, 0.2f, colorWhite, viewAxis );
end.z -= 16.0f;
gameRenderWorld->DrawText( va( "Active: %i Group ID: %i", botActions[ actionNumber ]->ActionIsActive(), botActions[ actionNumber ]->GetActionGroup() ), end, 0.2f, colorWhite, viewAxis );
end.z -= 16.0f;
gameRenderWorld->DrawText( va( "Vehicle: %i Deployable: %i", botActions[ actionNumber ]->GetActionVehicleType(), botActions[ actionNumber ]->GetDeployableType() ), end, 0.2f, colorWhite, viewAxis );
end.z -= 16.0f;
gameRenderWorld->DrawText( va( "TargetRouteID: %i BlindFire: %i", botActions[ actionNumber ]->routeID, botActions[ actionNumber ]->blindFire ), end, 0.2f, colorWhite, viewAxis );
end.z -= 16.0f;
gameRenderWorld->DrawText( va( "Valid Class: %i Goal Time: %i", botActions[ actionNumber ]->validClasses, botActions[ actionNumber ]->actionTimeInSeconds ), end, 0.2f, colorWhite, viewAxis );
end.z -= 16.0f;
gameRenderWorld->DrawText( va( "Team Owner: %i Has Obj: %i", botActions[ actionNumber ]->GetTeamOwner(), botActions[ actionNumber ]->hasObj ), end, 0.2f, colorWhite, viewAxis );
end.z -= 16.0f;
gameRenderWorld->DrawText( va( "Action State: %i Disguise Safe: %i", botActions[ actionNumber ]->actionState, botActions[ actionNumber ]->disguiseSafe ), end, 0.2f, colorWhite, viewAxis );
end.z -= 16.0f;
gameRenderWorld->DrawText( va( "No Hack: %i Requires Vehicle Type: %i", botActions[ actionNumber ]->noHack, botActions[ actionNumber ]->requiresVehicleType ), end, 0.2f, colorWhite, viewAxis );
end.z -= 16.0f;
gameRenderWorld->DrawText( va( "Is Priority: %i", botActions[ actionNumber ]->priority ), end, 0.2f, colorWhite, viewAxis );
if ( drawAllInfo ) {
if ( !botActions[ actionNumber ]->actionBBox.IsCleared() ) {
gameRenderWorld->DebugBox( colorRed, botActions[ actionNumber ]->actionBBox );
gameRenderWorld->DebugArrow( colorLtBlue, botActions[ actionNumber ]->actionBBox.GetCenter(), botActions[ actionNumber ]->origin, 16 );
}
gameRenderWorld->DebugCircle( colorBlue, botActions[ actionNumber ]->origin, idVec3( 0, 0, 1 ), botActions[ actionNumber ]->radius, 8 );
for( int j = 0; j < MAX_LINKACTIONS; j++ ) {
if ( botActions[ actionNumber ]->actionTargets[ j ].inuse == false ) {
continue;
}
end = botActions[ actionNumber ]->actionTargets[ j ].origin;
end.z += 64.0f;
gameRenderWorld->DebugLine( colorYellow, botActions[ actionNumber ]->actionTargets[ j ].origin, end, 16 );
gameRenderWorld->DebugCircle( colorOrange, botActions[ actionNumber ]->actionTargets[ j ].origin, idVec3( 0, 0, 1 ), botActions[ actionNumber ]->actionTargets[ j ].radius, 8 );
gameRenderWorld->DebugArrow( colorLtBlue, botActions[ actionNumber ]->actionTargets[ j ].origin, botActions[ actionNumber ]->origin, 16 );
}
}
}
/*
==================
idBotThreadData::DrawRearSpawns
==================
*/
void idBotThreadData::DrawRearSpawns() {
idPlayer *player = gameLocal.GetLocalPlayer();
if ( !player ) {
return;
}
idMat3 viewAxis = player->GetViewAxis();
sdTeamManagerLocal& manager = sdTeamManager::GetInstance();
sdTeamInfo* gdfTeam = &manager.GetTeamByIndex( GDF );
sdTeamInfo* stroggTeam = &manager.GetTeamByIndex( STROGG );
if ( gdfTeam == NULL || stroggTeam == NULL ) {
return;
}
idEntity* gdfHomeBaseSpawn = gdfTeam->GetHomeBaseSpawn();
idEntity* stroggHomeBaseSpawn = stroggTeam->GetHomeBaseSpawn();
if ( ( gdfHomeBaseSpawn == NULL || gdfHomeBaseSpawn->GetPhysics() == NULL ) && ( stroggHomeBaseSpawn == NULL || stroggHomeBaseSpawn->GetPhysics() == NULL ) ) {
return;
}
if ( gdfHomeBaseSpawn != NULL && gdfHomeBaseSpawn->GetPhysics() ) {
gameRenderWorld->DebugCircle( colorGreen, gdfHomeBaseSpawn->GetPhysics()->GetOrigin(), idVec3( 0, 0, 1 ), 64.0f, 32 );
gameRenderWorld->DebugCircle( colorBlue, gdfHomeBaseSpawn->GetPhysics()->GetOrigin(), idVec3( 0, 0, 1 ), MAX_VEHICLE_RANGE, 32 );
gameRenderWorld->DrawText( "GDF Rear Spawn", gdfHomeBaseSpawn->GetPhysics()->GetOrigin() + idVec3( 0, 0, 82 ), 0.60f, colorWhite, viewAxis );
gameRenderWorld->DrawText( va( "Percent: %i Used: %i", GetGameWorldState()->gameLocalInfo.teamUsesSpawnInfo[ GDF ].percentage, GetGameWorldState()->gameLocalInfo.teamUsesSpawnInfo[ GDF ].teamUsesSpawn ), gdfHomeBaseSpawn->GetPhysics()->GetOrigin(), 0.20f, colorWhite, viewAxis );
}
if ( stroggHomeBaseSpawn != NULL && stroggHomeBaseSpawn->GetPhysics() ) {
gameRenderWorld->DebugCircle( colorGreen, stroggHomeBaseSpawn->GetPhysics()->GetOrigin(), idVec3( 0, 0, 1 ), 64.0f, 32 );
gameRenderWorld->DebugCircle( colorBlue, stroggHomeBaseSpawn->GetPhysics()->GetOrigin(), idVec3( 0, 0, 1 ), MAX_VEHICLE_RANGE, 32 );
gameRenderWorld->DrawText( "STROGG Rear Spawn", stroggHomeBaseSpawn->GetPhysics()->GetOrigin() + idVec3( 0, 0, 82 ), 0.60f, colorWhite, viewAxis );
gameRenderWorld->DrawText( va( "Percent: %i Used: %i", GetGameWorldState()->gameLocalInfo.teamUsesSpawnInfo[ STROGG ].percentage, GetGameWorldState()->gameLocalInfo.teamUsesSpawnInfo[ STROGG ].teamUsesSpawn ), stroggHomeBaseSpawn->GetPhysics()->GetOrigin(), 0.20f, colorWhite, viewAxis );
}
}
/*
==================
idBotThreadData::FindDeliverActionNumber
==================
*/
int idBotThreadData::FindDeliverActionNumber() {
int deliverActionNum = ACTION_NULL;
for( int i = 0; i < botActions.Num(); i++ ) {
if ( !botActions[ i ]->ActionIsValid() ) {
continue;
}
if ( botActions[ i ]->GetObjForTeam( STROGG ) != ACTION_DELIVER && botActions[ i ]->GetObjForTeam( GDF ) != ACTION_DELIVER ) {
continue;
}
deliverActionNum = i;
break;
}
return deliverActionNum;
}
/*
==================
idBotThreadData::VehicleRouteThink
==================
*/
void idBotThreadData::VehicleRouteThink( bool setup, int& routeActionNumber, const idVec3& vehicleOrg ) {
if ( setup ) {
mcpRoutes.Clear();
for( int i = 0; i < botActions.Num(); i++ ) {
if ( !botActions[ i ]->ActionIsValid() ) {
continue;
}
if ( botActions[ i ]->GetObjForTeam( GDF ) == ACTION_MCP_ROUTE_MARKER ) {
mcpRoutes.Append( i );
}
}
if ( mcpRoutes.Num() > 0 ) {
int i = gameLocal.random.RandomInt( mcpRoutes.Num() );
routeActionNumber = mcpRoutes[ i ];
} else {
routeActionNumber = ACTION_NULL;
}
} else {
for( int i = 0; i < mcpRoutes.Num(); i++ ) {
if ( !CheckActionIsValid( mcpRoutes[ i ] ) ) {
continue;
}
idVec3 vec = botThreadData.botActions[ mcpRoutes[ i ] ]->GetActionOrigin() - vehicleOrg;
if ( vec.LengthSqr() < Square( botThreadData.botActions[ mcpRoutes[ i ] ]->GetRadius() ) ) {
routeActionNumber = ACTION_NULL;
break;
}
}
}
}
/*
==================
idBotThreadData::ManageBotClassesOnSmallServer
==================
*/
void idBotThreadData::ManageBotClassesOnSmallServer() {
bool changedClass = false;
for( int t = 0; t < MAX_TEAMS; t++ ) {
int weaponLoadOut = gameLocal.random.RandomInt( 2 );
int numDesiredCriticalClass;
int numDesiredMedicClass;
playerTeamTypes_t botTeam;
playerClassTypes_t criticalClass = NOCLASS;
playerClassTypes_t desiredBotClass = NOCLASS;
const sdDeclPlayerClass* pc;
if ( t == GDF ) {
botTeam = GDF;
criticalClass = GetGameWorldState()->botGoalInfo.team_GDF_criticalClass;
numDesiredCriticalClass = ( GetNumClientsOnTeam( GDF ) >= 7 ) ? 3 : 2;
numDesiredMedicClass = ( GetNumClientsOnTeam( GDF ) >= 7 ) ? 2 : 1;
} else if ( t == STROGG ) {
botTeam = STROGG;
criticalClass = GetGameWorldState()->botGoalInfo.team_STROGG_criticalClass;
numDesiredCriticalClass = ( GetNumClientsOnTeam( STROGG ) >= 7 ) ? 3 : 2;
numDesiredMedicClass = ( GetNumClientsOnTeam( STROGG ) >= 7 ) ? 2 : 1;
}
int numDesiredSoldierClass = ( criticalClass == MEDIC ) ? 2 : 1; //mal: if have lots of medics, then need more soldiers.
int numMedic = GetNumClassOnTeam( botTeam, MEDIC );
int numEng = GetNumClassOnTeam( botTeam, ENGINEER );
int numFOps = GetNumClassOnTeam( botTeam, FIELDOPS );
int numCovert = GetNumClassOnTeam( botTeam, COVERTOPS );
int numSoldier = GetNumClassOnTeam( botTeam, SOLDIER );
int numRLOnTeam = GetNumWeaponsOnTeam( botTeam, ROCKET );
int numHeavyOnTeam = GetNumWeaponsOnTeam( botTeam, HEAVY_MG );
int numCritical;
if ( criticalClass != NOCLASS && bot_doObjectives.GetBool() ) {
numCritical = botThreadData.GetNumClassOnTeam( botTeam, criticalClass );
} else {
numCritical = -1;
}
//mal: make sure we always have a couple bots of the critical class first, unless the human wants to do the obj.
if ( numCritical < numDesiredCriticalClass && numCritical != -1 ) {
desiredBotClass = criticalClass;
} else if ( numMedic < numDesiredMedicClass ) { //mal: medics are incredibly useful. On a full server - have more.
desiredBotClass = MEDIC;
} else if ( numSoldier < numDesiredSoldierClass ) {
desiredBotClass = SOLDIER;
} else if ( numEng == 0 ) {
desiredBotClass = ENGINEER;
} else if ( numCovert == 0 ) {
desiredBotClass = COVERTOPS;
} else if ( numFOps == 0 ) {
desiredBotClass = FIELDOPS;
}
if ( desiredBotClass == NOCLASS ) {
continue;
}
playerClassTypes_t sampleClass = NOCLASS;
if ( numMedic > numDesiredMedicClass && ( criticalClass != MEDIC || numMedic > numDesiredCriticalClass ) ) {
sampleClass = MEDIC;
} else if ( numSoldier > numDesiredSoldierClass && ( criticalClass != SOLDIER || numSoldier > numDesiredCriticalClass ) ) {
sampleClass = SOLDIER;
} else if ( numEng > 1 && ( criticalClass != ENGINEER || numEng > numDesiredCriticalClass ) ) {
sampleClass = ENGINEER;
} else if ( numCovert > 1 && ( criticalClass != COVERTOPS || numCovert > numDesiredCriticalClass ) ) {
sampleClass = COVERTOPS;
} else if ( numFOps > 1 && ( criticalClass != FIELDOPS || numFOps > numDesiredCriticalClass ) ) {
sampleClass = FIELDOPS;
}
if ( sampleClass == NOCLASS ) { //mal: dont have anyone to spare.
continue;
}
if ( t == GDF ) {
if ( desiredBotClass == MEDIC ) {
pc = gameLocal.declPlayerClassType[ "medic" ];
} else if ( desiredBotClass == SOLDIER ) {
pc = gameLocal.declPlayerClassType[ "soldier" ];
if ( numRLOnTeam == 0 ) {
weaponLoadOut = 1;
} else if ( numHeavyOnTeam == 0 ) {
weaponLoadOut = 2;
} else {
weaponLoadOut = gameLocal.random.RandomInt( 4 );
}
} else if ( desiredBotClass == ENGINEER ) {
pc = gameLocal.declPlayerClassType[ "engineer" ];
} else if ( desiredBotClass == FIELDOPS ) {
pc = gameLocal.declPlayerClassType[ "fieldops" ];
weaponLoadOut = 0;
} else if ( desiredBotClass == COVERTOPS ) {
pc = gameLocal.declPlayerClassType[ "covertops" ];
} else {
continue;
}
} else if ( t == STROGG ) {
if ( desiredBotClass == MEDIC ) {
pc = gameLocal.declPlayerClassType[ "technician" ];
} else if ( desiredBotClass == SOLDIER ) {
pc = gameLocal.declPlayerClassType[ "aggressor" ];
if ( numRLOnTeam == 0 ) {
weaponLoadOut = 1;
} else if ( numHeavyOnTeam == 0 ) {
weaponLoadOut = 2;
} else {
weaponLoadOut = gameLocal.random.RandomInt( 4 );
}
} else if ( desiredBotClass == ENGINEER ) {
pc = gameLocal.declPlayerClassType[ "constructor" ];
} else if ( desiredBotClass == FIELDOPS ) {
pc = gameLocal.declPlayerClassType[ "oppressor" ];
weaponLoadOut = 0;
} else if ( desiredBotClass == COVERTOPS ) {
pc = gameLocal.declPlayerClassType[ "infiltrator" ];
} else {
continue;
}
}
for( int i = 0; i < MAX_CLIENTS; i++ ) {
clientInfo_t& playerInfo = GetGameWorldState()->clientInfo[ i ];
if ( !playerInfo.inGame ) {
continue;
}
if ( playerInfo.team != botTeam ) {
continue;
}
if ( playerInfo.classType != sampleClass ) {
continue;
}
if ( !playerInfo.isBot ) {
continue;
}
if ( playerInfo.isActor ) {
continue;
}
if ( !gameLocal.rules->IsWarmup() && playerInfo.health > 0 ) {
if ( ( playerInfo.lastClassChangeTime + MIN_CLASS_CHANGE_DELAY ) > gameLocal.time ) {
continue;
}
if ( bots[ i ]->GetAIState() != LTG ) { //mal: now, make sure the bot isn't doing something important.
continue;
}
if ( playerInfo.classType != criticalClass ) {
if ( bots[ i ]->GetLTGType() != ROAM_GOAL && bots[ i ]->GetLTGType() != CAMP_GOAL ) {
if ( bots[ i ]->GetLTGType() == DEFENSE_CAMP_GOAL && random.RandomInt( 100 ) > 50 ) { //mal: if defense camping, a random chance we might do the switch.
continue;
} else {
continue;
}
}
}
}
idPlayer* player = gameLocal.GetClient( i );
if ( player == NULL ) {
continue;
}
player->ChangeClass( pc, weaponLoadOut );
if ( player->GetHealth() > 0 ) {
player->Kill( NULL ); //mal: hurry up and suicide, and get back into the game as our new class!
}
playerInfo.lastClassChangeTime = gameLocal.time;
changedClass = true;
break;
}
}
if ( !gameLocal.rules->IsWarmup() ) {
if ( ( lastWeapChangedTime + MIN_WEAPON_CHANGE_DELAY ) > gameLocal.time ) {
return;
}
}
if ( changedClass == false || gameLocal.rules->IsWarmup() ) { //mal: make sure there is at least 1 RL soldier on each team.
for( int t = 0; t < MAX_TEAMS; t++ ) {
playerTeamTypes_t botTeam;
const sdDeclPlayerClass* pc;
if ( t == GDF ) {
botTeam = GDF;
} else if ( t == STROGG ) {
botTeam = STROGG;
}
int numSoldier = GetNumClassOnTeam( botTeam, SOLDIER );
if ( numSoldier == 0 ) {
continue;
}
int numRLOnTeam = GetNumWeaponsOnTeam( botTeam, ROCKET );
bool skipRLSoldiers = true;
int switchWeaponNumber = 1;
if ( numRLOnTeam > 0 ) {
if ( numSoldier > 1 ) { //mal: with 1 or more soldiers, make sure have at least one heavy weapon.
int numHeavyOnTeam = GetNumWeaponsOnTeam( botTeam, HEAVY_MG );
if ( numHeavyOnTeam > 0 ) {
continue;
}
if ( numRLOnTeam > 1 ) {
skipRLSoldiers = false; //mal: have enough RLs on the map to borrow from them.
}
switchWeaponNumber = 2;
} else {
continue;
}
}
if ( t == GDF ) {
pc = gameLocal.declPlayerClassType[ "soldier" ];
} else if ( t == STROGG ) {
pc = gameLocal.declPlayerClassType[ "aggressor" ];
}
for( int i = 0; i < MAX_CLIENTS; i++ ) {
clientInfo_t& playerInfo = GetGameWorldState()->clientInfo[ i ];
if ( !playerInfo.inGame ) {
continue;
}
if ( playerInfo.team != botTeam ) {
continue;
}
if ( playerInfo.classType != SOLDIER ) {
continue;
}
if ( !playerInfo.isBot ) {
continue;
}
if ( skipRLSoldiers ) {
if ( playerInfo.weapInfo.primaryWeapon == ROCKET ) {
continue;
}
}
idPlayer* player = gameLocal.GetClient( i );
if ( player == NULL ) {
continue;
}
player->ChangeClass( pc, switchWeaponNumber );
lastWeapChangedTime = gameLocal.time;
if ( gameLocal.rules->IsWarmup() ) {
player->Kill( NULL ); //mal: hurry up and suicide, and get back into the game with our new weapon!
}
break;
}
}
}
if ( !checkedLastMapStageCovertWeapons && GetGameWorldState()->botGoalInfo.gameIsOnFinalObjective ) { //mal: on the last map obj, if theres no spots for snipers, turn in our sniper rifles.
bool gdfSnipersExist = false;
bool stroggSnipersExist = false;
checkedLastMapStageCovertWeapons = true;
for( int i = 0; i < botActions.Num(); i++ ) {
if ( !botActions[ i ]->ActionIsActive() ) {
continue;
}
if ( !botActions[ i ]->ActionIsValid() ) {
continue;
}
if ( botActions[ i ]->GetHumanObj() == ACTION_SNIPE ) {
gdfSnipersExist = true;
}
if ( botActions[ i ]->GetStroggObj() == ACTION_SNIPE ) {
stroggSnipersExist = true;
}
if ( gdfSnipersExist && stroggSnipersExist ) {
break;
}
}
for( int t = 0; t < MAX_TEAMS; t++ ) {
playerTeamTypes_t botTeam;
if ( t == GDF ) {
if ( gdfSnipersExist ) {
continue;
}
botTeam = GDF;
} else if ( t == STROGG ) {
if ( stroggSnipersExist ) {
continue;
}
botTeam = STROGG;
}
int numCoverts = GetNumClassOnTeam( botTeam, COVERTOPS );
if ( numCoverts == 0 ) {
continue;
}
int numSnipersOnTeam = GetNumWeaponsOnTeam( botTeam, SNIPERRIFLE );
if ( numSnipersOnTeam == 0 ) {
continue;
}
const sdDeclPlayerClass* pc;
if ( t == GDF ) {
pc = gameLocal.declPlayerClassType[ "covertops" ];
} else if ( t == STROGG ) {
pc = gameLocal.declPlayerClassType[ "infiltrator" ];
}
for( int i = 0; i < MAX_CLIENTS; i++ ) {
clientInfo_t& playerInfo = GetGameWorldState()->clientInfo[ i ];
if ( !playerInfo.inGame ) {
continue;
}
if ( playerInfo.team != botTeam ) {
continue;
}
if ( playerInfo.classType != COVERTOPS ) {
continue;
}
if ( !playerInfo.isBot ) {
continue;
}
idPlayer* player = gameLocal.GetClient( i );
if ( player == NULL ) {
continue;
}
player->ChangeClass( pc, 0 );
}
}
}
}
/*
==================
idBotThreadData::EntityIsExplosiveCharge
==================
*/
bool idBotThreadData::EntityIsExplosiveCharge( int entNum, bool armedOnly, const playerTeamTypes_t& ignoreTeam ) {
bool isCharge = false;
for( int i = 0; i < MAX_CHARGES; i++ ) {
if ( GetGameWorldState()->chargeInfo[ i ].entNum != entNum ) {
continue;
}
if ( armedOnly ) {
if ( GetGameWorldState()->chargeInfo[ i ].state != BOMB_ARMED ) {
continue;
}
}
if ( ignoreTeam != NOTEAM ) {
if ( GetGameWorldState()->chargeInfo[ i ].team == ignoreTeam ) {
continue;
}
}
isCharge = true;
break;
}
return isCharge;
}
/*
============
idBotThreadData::MapHasAnActor
============
*/
bool idBotThreadData::MapHasAnActor() {
bool hasActor = false;
for( int i = 0; i < MAX_CLIENTS; i++ ) {
if ( !ClientIsValid( i ) ) {
continue;
}
const clientInfo_t& playerInfo = GetGameWorldState()->clientInfo[ i ];
if ( !playerInfo.inGame ) {
continue;
}
if ( !playerInfo.isActor ) {
continue;
}
hasActor = true;
break;
}
return hasActor;
}
/*
============
idBotThreadData::FindBotToBeActor
============
*/
void idBotThreadData::FindBotToBeActor() {
if ( !GetGameWorldState()->gameLocalInfo.teamGDFHasHuman ) {
return;
}
if ( actorMissionInfo.playerIsOnFinalMission && !actorMissionInfo.playerNeedsFinalBriefing ) {
return;
}
for( int i = 0; i < MAX_CLIENTS; i++ ) {
if ( !ClientIsValid( i ) ) {
continue;
}
clientInfo_t& playerInfo = GetGameWorldState()->clientInfo[ i ];
if ( !playerInfo.inGame ) {
continue;
}
if ( !playerInfo.isBot ) {
continue;
}
if ( playerInfo.team != GDF ) {
continue;
}
if ( playerInfo.classType != MEDIC ) {
continue;
}
playerInfo.isActor = true;
actorMissionInfo.actorClientNum = i;
idPlayer* player = gameLocal.GetClient( i );
if ( player != NULL ) {
player->SetGodMode( true );
networkSystem->ServerSetBotUserName( i, trainingGDFBotNames[ BOT_GUIDE_NAME_SLOT ] );
}
break;
}
}
/*
============
idBotThreadData::TrainingThink
Quick and dirty func to track the progress of the training
============
*/
void idBotThreadData::TrainingThink() {
if ( !actorMissionInfo.setup ) {
for( int i = 0; i < MAX_CLIENTS; i++ ) {
if ( !ClientIsValid( i ) ) {
continue;
}
const clientInfo_t& playerInfo = GetGameWorldState()->clientInfo[ i ];
if ( !playerInfo.inGame ) {
continue;
}
if ( playerInfo.isBot ) {
continue;
}
if ( playerInfo.team != GDF ) {
continue;
}
actorMissionInfo.setup = true;
actorMissionInfo.targetClientNum = i;
break;
}
int actionNum = botThreadData.FindActionByName( "hut_avt_deploy" );
if ( actionNum != -1 ) {
botThreadData.ignoreActionNumber = actionNum;
}
}
if ( actorMissionInfo.deployableStageIsActive ) {
for( int i = 0; i < MAX_DEPLOYABLES; i++ ) {
if ( GetGameWorldState()->deployableInfo[ i ].entNum == 0 ) {
continue;
}
if ( GetGameWorldState()->deployableInfo[ i ].ownerClientNum != actorMissionInfo.targetClientNum ) {
continue;
}
sdObjectiveManager::GetInstance().PlayerDeployableDeployed();
actorMissionInfo.deployableStageIsActive = false;
break;
}
}
if ( actorMissionInfo.targetClientNum > -1 && actorMissionInfo.targetClientNum < MAX_CLIENTS ) {
idPlayer* player = gameLocal.GetClient( actorMissionInfo.targetClientNum );
idWeapon* gun = player->weapon.GetEntity(); //mal: get some info on our gun
if ( gun ) {
gun->UpdateVisibility();
}
}
if ( actorMissionInfo.actorClientNum > -1 && actorMissionInfo.actorClientNum < MAX_CLIENTS ) {
idPlayer* player = gameLocal.GetClient( actorMissionInfo.actorClientNum );
if ( player != NULL ) {
player->SetGodMode( true );
sdInventory& inventory = player->GetInventory();
for( int i = 0; i < gameLocal.declAmmoTypeType.Num(); i++ ) {
inventory.SetAmmo( i, inventory.GetMaxAmmo( i ) );
}
}
}
if ( gameLocal.numClients == bot_minClients.GetInteger() && actorMissionInfo.setupBotNames ) {
int gdfCounter = BOT_GUIDE_NAME_SLOT + 1;
int stroggCounter = 0;
for( int i = 0; i < MAX_CLIENTS; i++ ) {
if ( !ClientIsValid( i ) ) {
continue;
}
const clientInfo_t& playerInfo = GetGameWorldState()->clientInfo[ i ];
if ( !playerInfo.inGame ) {
continue;
}
if ( !playerInfo.isBot ) {
continue;
}
if ( playerInfo.isActor ) { //mal: this is setup when we first set the actor.
continue;
}
if ( playerInfo.team == GDF && gdfCounter < trainingGDFBotNames.Num() ) {
networkSystem->ServerSetBotUserName( i, trainingGDFBotNames[ gdfCounter ] );
gdfCounter++;
} else if ( playerInfo.team == STROGG && stroggCounter < trainingSTROGGBotNames.Num() ) {
networkSystem->ServerSetBotUserName( i, trainingSTROGGBotNames[ stroggCounter ] );
stroggCounter++;
}
actorMissionInfo.setupBotNames = false;
}
}
bot_doObjsInTrainingMode.SetBool( false );
}
/*
============
idBotThreadData::PlayerIsBeingBriefed
============
*/
bool idBotThreadData::PlayerIsBeingBriefed() {
if ( GetGameWorldState()->botGoalInfo.isTrainingMap == false ) {
return false;
}
if ( actorMissionInfo.targetClientNum > -1 && actorMissionInfo.targetClientNum < MAX_CLIENTS ) {
idPlayer* player = gameLocal.GetClient( actorMissionInfo.targetClientNum );
if ( player != NULL ) {
if ( player->IsBeingBriefed() ) {
return true;
}
}
}
return false;
}
/*
============
idBotThreadData::DeactivateBadMapActions
This is a nasty, 11th hour, 59th minute, 59th second hack! No time to remove these actions ingame, so just disable them.
============
*/
void idBotThreadData::DeactivateBadMapActions() {
if ( GetGameWorldState()->gameLocalInfo.gameMap == QUARRY ) { //mal: these actions face the GDF spawn, and their APTs - leading to MANY Strogg deaths.
int actionNumber = botThreadData.FindActionByName( "strogg_jammer_3" );
if ( actionNumber != -1 ) {
botActions[ actionNumber ]->SetActive( false );
botActions[ actionNumber ]->SetHumanObj( ACTION_NULL );
botActions[ actionNumber ]->SetStroggObj( ACTION_NULL );
botActions[ actionNumber ]->SetActionGroupNum( -1 );
} else {
Warning( "No valid bot action found by the name strogg_jammer_3!" );
}
actionNumber = botThreadData.FindActionByName( "strogg_jammer_4" );
if ( actionNumber != -1 ) {
botActions[ actionNumber ]->SetActive( false );
botActions[ actionNumber ]->SetHumanObj( ACTION_NULL );
botActions[ actionNumber ]->SetStroggObj( ACTION_NULL );
botActions[ actionNumber ]->SetActionGroupNum( -1 );
} else {
Warning( "No valid bot action found by the name strogg_jammer_4!" );
}
}
}
/*
============
idBotThreadData::CheckIfClientsInGameYet
============
*/
bool idBotThreadData::CheckIfClientsInGameYet() {
bool hasPlayers = false;
for( int i = 0; i < MAX_CLIENTS; i++ ) {
if ( !GetBotWorldState()->clientInfo[ i ].inGame ) {
continue;
}
if ( GetBotWorldState()->clientInfo[ i ].classType == NOCLASS ) {
continue;
}
idPlayer* player = gameLocal.GetClient( i );
if ( player == NULL ) {
continue;
}
if ( !player->CanPlay() ) {
continue;
}
hasPlayers = true;
break;
}
return hasPlayers;
}