6418 lines
202 KiB
C++
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;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|