/* ================ idBotAI::PushAINodeOntoStack ================ */ void idBotAI::PushAINodeOntoStack( int clientNum, int entNum, int actionNumber, int timeLimit, bool isPriority, bool useVehicle, bool useRoute ) { AIStack.STACK_AI_NODE = LTG_AI_SUB_NODE; AIStack.VEHICLE_STACK_AI_NODE = V_LTG_AI_SUB_NODE; AIStack.stackLTGType = ltgType; AIStack.vehicleStackLTGType = vLTGType; if ( ClientIsValid( clientNum, -1 ) ) { AIStack.stackClientNum = clientNum; AIStack.stackClientSpawnID = botWorld->clientInfo[ clientNum ].spawnID; } AIStack.stackEntNum = entNum; AIStack.stackTimeLimit = botWorld->gameLocalInfo.time + timeLimit; AIStack.stackActionNum = actionNumber; AIStack.isPriority = isPriority; AIStack.useVehicle = useVehicle; if ( useRoute ) { if ( routeNode != NULL ) { AIStack.routeNode = routeNode; } else { assert( false ); } } else { AIStack.routeNode = NULL; } }
/* ================== idBotAI::VehicleHasGunnerSeatOpen ================== */ bool idBotAI::VehicleHasGunnerSeatOpen( int entNum ) { bool hasSeat = true; int i; int j = 0; int numSeats = 1; proxyInfo_t vehicleInfo; botVehicleWeaponInfo_t weaponType = MINIGUN; GetVehicleInfo( entNum, vehicleInfo ); if ( !vehicleInfo.hasFreeSeat ) { //mal: that was fast.... return false; } if ( vehicleInfo.type == MCP ) { return true; } if ( vehicleInfo.type == BUFFALO ) { //mal: buffalo actually has 2 gunner seats, where most vehicles only have 1. numSeats = 3; } if ( vehicleInfo.type == TROJAN ) { weaponType = LAW; } for( i = 0; i < MAX_CLIENTS; i++ ) { if ( i == botNum && botVehicleInfo == NULL ) { continue; } if ( !ClientIsValid( i, -1 ) ) { continue; } const clientInfo_t &playerInfo = botWorld->clientInfo[ i ]; if ( playerInfo.team != botInfo->team ) { continue; } if ( playerInfo.proxyInfo.entNum != entNum ) { continue; } if ( playerInfo.proxyInfo.weapon != weaponType ) { continue; } j++; if ( j >= numSeats ) { hasSeat = false; break; } } return hasSeat; }
/* =============== idBotAI::Bot_CheckDelayedChats Checks to see if there are any delayed chats ready to play. =============== */ void idBotAI::Bot_CheckDelayedChats() { bool hasChat = false; int i; idVec3 vec; for( i = 0; i < MAX_DELAYED_CHATS; i++ ) { if ( delayedChats[ i ].delayTime == 0 || delayedChats[ i ].delayTime > botWorld->gameLocalInfo.time ) { continue; } //mal: this chat is currently tagged released or not ready to be sent, so find another. botUcmd->desiredChat = delayedChats[ i ].chat; botUcmd->desiredChatForced = delayedChats[ i ].forceChat; delayedChats[ i ].delayTime = 0; hasChat = true; break; } //mal: if we dont have anything important to say, lets see if we should say "Your Welcome!" to someone who thanked us. if ( hasChat == false && botInfo->health > 0 && botInfo->lastChatTime[ YOURWELCOME ] < botWorld->gameLocalInfo.time ) { if ( ( botInfo->classType == MEDIC || botInfo->classType == FIELDOPS ) && aiState == LTG || aiState == NBG ) { for( i = 0; i < MAX_CLIENTS; i++ ) { if ( !ClientIsValid( i, -1 ) ) { continue; } if ( i == botNum ) { continue; } const clientInfo_t& playerInfo = botThreadData.GetBotWorldState()->clientInfo[ i ]; if ( playerInfo.health <= 0 ) { //mal: seems retared to say "Bitte Sehr!" to someones whos' dead! continue; } if ( playerInfo.myHero != botNum ) { continue; } if ( playerInfo.lastThanksTime + 5000 < botWorld->gameLocalInfo.time || playerInfo.lastThanksTime + 1500 > botWorld->gameLocalInfo.time ) { continue; } vec = playerInfo.origin - botInfo->origin; if ( vec.LengthSqr() > Square( 900.0f ) ) { continue; } botUcmd->desiredChat = YOURWELCOME; } } } }
/* ================ idBotAI::COMBAT_Vehicle_AttackEnemy ================ */ bool idBotAI::COMBAT_Vehicle_AttackEnemy() { bool keepEnemy = true; if ( !enemyInfo.enemyVisible && enemyInfo.enemyLastVisTime + 5000 < botWorld->gameLocalInfo.time ) { if ( !Bot_ShouldVehicleChaseHiddenEnemy() ) { Bot_ResetEnemy(); return false; } Bot_PickVehicleChaseType(); return false; } Bot_PickBestVehicleWeapon(); if ( vehicleUpdateTime < botWorld->gameLocalInfo.time ) { if ( VEHICLE_COMBAT_MOVEMENT_STATE == NULL ) { keepEnemy = Bot_FindBestVehicleCombatMovement(); } } if ( !keepEnemy ) { //mal: make sure enemy is reachable by our current move state/abilities/limitations. If not, we have to forget them. Bot_IgnoreEnemy( enemy, 3000 ); //mal: perhaps in 3 seconds, we will have moved to a better position for a kill... Bot_ResetEnemy(); return false; } if ( VEHICLE_COMBAT_MOVEMENT_STATE != NULL ) { CallFuncPtr( VEHICLE_COMBAT_MOVEMENT_STATE ); } if ( ClientIsValid( enemy, enemySpawnID ) && enemyInfo.enemyVisible ) { if ( botVehicleInfo->type <= ICARUS || botInfo->proxyInfo.weapon == MINIGUN ) { Bot_LookAtEntity( enemy, AIM_TURN ); //mal: aim at the enemy - but let the game side code handle it. Bot_CheckVehicleAttack(); } } return false; }
/* ================ idBotAI::Bot_VehicleFindBetterEnemy ================ */ bool idBotAI::Bot_VehicleFindBetterEnemy() { int i; int entClientNum = enemy; float dist; float entDist; proxyInfo_t vehicleInfo; proxyInfo_t enemyVehicleInfo; idVec3 vec; if ( enemy == -1 ) { //mal: we lost our enemy for some reason, so just skip finding a new one til next frame. return false; } if ( ignoreNewEnemiesWhileInVehicleTime > botWorld->gameLocalInfo.time ) { return false; } if ( botWorld->clientInfo[ enemy ].proxyInfo.entNum != CLIENT_HAS_NO_VEHICLE ) { //mal: if we're attacking the MCP, its the priority. if ( botWorld->clientInfo[ enemy ].proxyInfo.entNum == botWorld->botGoalInfo.botGoal_MCP_VehicleNum ) { return false; } } GetVehicleInfo( botWorld->clientInfo[ enemy ].proxyInfo.entNum, enemyVehicleInfo ); if ( enemyVehicleInfo.entNum > 0 && ( enemyVehicleInfo.type == ANANSI || enemyVehicleInfo.type == HORNET ) ) { //mal: stay in dogfights! return false; } const clientInfo_t& enemyPlayerInfo = botWorld->clientInfo[ enemy ]; vec = enemyPlayerInfo.origin - botInfo->origin; entDist = vec.LengthSqr(); if ( !enemyInfo.enemyVisible ) { //mal: if we can't see our current enemy, more likely to attack a visible enemy. entDist = idMath::INFINITY; } if ( botWorld->gameLocalInfo.botSkill == BOT_SKILL_DEMO && !botWorld->botGoalInfo.gameIsOnFinalObjective && ( !enemyPlayerInfo.isBot || enemyPlayerInfo.isActor ) ) { //mal: dont worry about keeping our human target in training mode, unless its the final obj... entDist += Square( TRAINING_MODE_RANGE_ADDITION ); } bool curEnemyNotInVehicle = false; if ( botWorld->clientInfo[ enemy ].proxyInfo.entNum == CLIENT_HAS_NO_VEHICLE && !ClientIsDefusingOurTeamCharge( enemy ) && !vehicleEnemyWasInheritedFromFootCombat ) { //mal: if our current enemy is on foot, more likely to pick a better target. Unless they're defusing our charge, or an enemy we jumped in this vehicle for, then we artificially raise their importance. curEnemyNotInVehicle = true; } numVisEnemies = 1; //mal: our current enemy is always visible to us for ( i = 0; i < MAX_CLIENTS; i++ ) { if ( !ClientIsValid( i, -1 ) ) { continue; //mal: no valid client in this client slot! } if ( i == botNum ) { continue; //mal: dont try to fight ourselves! } if ( i == enemy ) { //mal: ignore an enemy we already have continue; } if ( EnemyIsIgnored( i ) ) { continue; //mal: dont try to fight someone we've flagged to ignore for whatever reason! } //mal: if we're in the middle of a critical obj, dont go looking for trouble, unless they're shooting us! if ( Client_IsCriticalForCurrentObj( botNum, -1.0f ) && ( botInfo->lastAttacker != i || botInfo->lastAttackerTime + 3000 < botWorld->gameLocalInfo.time ) && !ClientIsDefusingOurTeamCharge( i ) ) { continue; } const clientInfo_t& playerInfo = botWorld->clientInfo[ i ]; if ( playerInfo.isNoTarget ) { continue; } //mal: dont target clients that have notarget set - this is useful for debugging, etc. if ( playerInfo.isDisguised && playerInfo.disguisedClient != botNum ) { continue; //mal: won't "see" disguised clients, unless they look like us! } if ( playerInfo.inLimbo ) { continue; } if ( playerInfo.isActor ) { continue; } if ( playerInfo.invulnerableEndTime > botWorld->gameLocalInfo.time ) { continue; //mal: ignore revived/just spawned in clients - get the ppl around them! } if ( playerInfo.health <= 0 ) { continue; } if ( playerInfo.team == botInfo->team ) { continue; } if ( botWorld->gameLocalInfo.botSkill == BOT_SKILL_DEMO && !botWorld->botGoalInfo.gameIsOnFinalObjective && !playerInfo.isBot && enemyPlayerInfo.isBot ) { //mal: dont worry about human targets in training mode if we have a bot one, unless its the final obj... continue; } if ( botWorld->gameLocalInfo.botSkill > BOT_SKILL_EASY ) { //mal: st00pid bot - your not smart enough to pick your targets wisely if ( enemyVehicleInfo.entNum > 0 && enemyInfo.enemyVisible ) { if ( !( enemyVehicleInfo.flags & PERSONAL ) && !( enemyVehicleInfo.flags & WATER ) && ( enemyVehicleInfo.isAirborneVehicle && enemyVehicleInfo.xyspeed > 900.0f && entDist > Square( 2000.0f ) ) ) { if ( playerInfo.proxyInfo.entNum == CLIENT_HAS_NO_VEHICLE && !Client_IsCriticalForCurrentObj( i, 1500.0f ) && !ClientHasObj( i ) ) { continue; } //mal: dont worry about an enemy in a vehicle if the vehicle is far away, moving too fast, is not a real threat } } } //mal: if our current enemy is in a vehicle, and this guy isn't, and this guy doesn't have an obj, or isn't important, hes not worth fighting. bool enemyIsInAirAttackVehicle = false; if ( playerInfo.proxyInfo.entNum != CLIENT_HAS_NO_VEHICLE ) { //mal: pick the driver of a vehicle as the target, NOT passengers, unless there is no driver - then kill whoever. GetVehicleInfo( playerInfo.proxyInfo.entNum, vehicleInfo ); if ( vehicleInfo.driverEntNum != i && vehicleInfo.driverEntNum != -1 ) { continue; } if ( vehicleInfo.type == ANANSI || vehicleInfo.type == HORNET ) { enemyIsInAirAttackVehicle = true; } else { vec = vehicleInfo.origin - botInfo->origin; if ( vehicleInfo.xyspeed > 600.0f && vec.LengthSqr() > Square( 1900.0f ) && !InFrontOfVehicle( vehicleInfo.entNum, botInfo->origin ) ) { //mal: if they're in a mad dash away from us, forget about them! continue; } } } vec = playerInfo.origin - botInfo->origin; dist = vec.LengthSqr(); if ( !enemyIsInAirAttackVehicle ) { if ( dist > Square( ENEMY_SIGHT_BUSY_DIST * 2.0f ) ) { continue; } } float tempDist = entDist; if ( curEnemyNotInVehicle && playerInfo.proxyInfo.entNum != CLIENT_HAS_NO_VEHICLE ) { tempDist += Square( 3000.0f ); } if ( !enemyIsInAirAttackVehicle ) { if ( dist > tempDist ) { continue; } } else { if ( dist > Square( AIRCRAFT_ATTACK_DIST ) ) { continue; } } if ( !ClientIsVisibleToBot ( i, true, false ) ) { continue; } if ( Client_IsCriticalForCurrentObj( i, 1500.0f ) && botWorld->gameLocalInfo.botSkill > BOT_SKILL_EASY ) { //mal: if a critical class, get high priority dist = Square( 600.0f ); ignoreNewEnemiesWhileInVehicleTime = botWorld->gameLocalInfo.time + IGNORE_NEW_ENEMIES_TIME; } if ( ClientHasObj( i ) && botWorld->gameLocalInfo.botSkill > BOT_SKILL_EASY ) { //mal: if have docs, get HIGHER priority. dist = Square( 500.0f ); ignoreNewEnemiesWhileInVehicleTime = botWorld->gameLocalInfo.time + IGNORE_NEW_ENEMIES_TIME; } if ( ClientIsDefusingOurTeamCharge( i ) && botWorld->gameLocalInfo.botSkill > BOT_SKILL_EASY ) { //mal: if defusing our charge, get HIGHER priority. dist = Square( 100.0f ); ignoreNewEnemiesWhileInVehicleTime = botWorld->gameLocalInfo.time + IGNORE_NEW_ENEMIES_TIME; } if ( botWorld->botGoalInfo.mapHasMCPGoal && botWorld->gameLocalInfo.botSkill > BOT_SKILL_EASY ) { if ( playerInfo.proxyInfo.entNum == botWorld->botGoalInfo.botGoal_MCP_VehicleNum ) { dist = Square( 400.0f ); ignoreNewEnemiesWhileInVehicleTime = botWorld->gameLocalInfo.time + IGNORE_NEW_ENEMIES_TIME; } } if ( enemyIsInAirAttackVehicle && botWorld->gameLocalInfo.botSkill > BOT_SKILL_EASY ) { //mal: dont ignore a chance to dogfight! dist = Square( 100.0f ); ignoreNewEnemiesWhileInVehicleTime = botWorld->gameLocalInfo.time + IGNORE_NEW_ENEMIES_TIME; } numVisEnemies++; entClientNum = i; entDist = dist; } if ( entClientNum != enemy ) { enemy = entClientNum; enemySpawnID = botWorld->clientInfo[ entClientNum ].spawnID; enemyInfo.enemy_FS_Pos = botWorld->clientInfo[ entClientNum ].origin; enemyInfo.enemy_LS_Pos = enemyInfo.enemy_FS_Pos; enemyInfo.enemyLastVisTime = botWorld->gameLocalInfo.time; enemyAcquireTime = botWorld->gameLocalInfo.time; bot_FS_Enemy_Pos = botInfo->origin; bot_LS_Enemy_Pos = bot_FS_Enemy_Pos; VEHICLE_COMBAT_AI_SUB_NODE = NULL; //mal: reset the bot's combat AI node and movement state. COMBAT_MOVEMENT_STATE = NULL; return true; } return false; }
/* ================ idBotAI::Bot_VehicleFindEnemy We'll sort thru the clients, and ignore certain clients if we're too busy to be buggered (carrying obj, planting/hacking, etc) or they're not valid enemies (in disguise, hidden by smoke, etc). ================ */ bool idBotAI::Bot_VehicleFindEnemy() { bool hasAttackedMate; bool hasAttackedCriticalMate; bool hasObj; bool isDefusingOurBomb; bool inFront; bool botGotShotRecently; bool botIsBigShot; bool audible; bool isVisible; bool isFacingUs; bool isFiringWeapon; bool isNearOurObj; bool isAttackingDeployable = false; bool inAttackAirCraft = false; int i; int entClientNum = -1; float dist; float botSightDist; float tempSightDist; float entDist = idMath::INFINITY; proxyInfo_t enemyVehicleInfo; enemyVehicleInfo.entNum = 0; idVec3 vec; numVisEnemies = 0; vehicleEnemyWasInheritedFromFootCombat = false; botSightDist = Square( ENEMY_VEHICLE_SIGHT_DIST ); //mal_FIXME: break this out into a script cmd! /* if ( botSightDist > Square( 8000.0f ) ) { botSightDist = Square( 8000.0f ); } else if ( botSightDist < Square( 3000.0f ) ) { botSightDist = Square( 3000.0f ); } */ //mal: some debugging stuff.... if ( botWorld->gameLocalInfo.botIgnoreEnemies == 1 ) { return false; } else if ( botWorld->gameLocalInfo.botIgnoreEnemies == 2 ) { if ( botInfo->team == GDF ) { return false; } } else if ( botWorld->gameLocalInfo.botIgnoreEnemies == 3 ) { if ( botInfo->team == STROGG ) { return false; } } if ( botVehicleInfo->type != BUFFALO && Bot_VehicleIsUnderAVTAttack() != -1 && ( ( botVehicleInfo->flags & ARMOR ) || botVehicleInfo->type > ICARUS ) ) { return false; } if ( botVehicleInfo->type > ICARUS ) { botSightDist = Square( 6000.0f ); } if ( botVehicleInfo->type == BUFFALO ) { botSightDist = Square( 3500.0f ); } if ( botVehicleInfo->type == GOLIATH || botVehicleInfo->type == DESECRATOR ) { //mal: these 2 are really limited botSightDist = Square( PLASMA_CANNON_RANGE - 1000.0f ); } if ( botVehicleInfo->type == HUSKY ) { //mal: we're no match for anybody! return false; } #ifdef _XENON if ( botVehicleInfo->type == MCP && botVehicleInfo->driverEntNum == botNum ) { return false; } if ( botVehicleInfo->type == PLATYPUS && botVehicleInfo->driverEntNum == botNum ) { return false; } #endif if ( botVehicleInfo->type > ICARUS && Client_IsCriticalForCurrentObj( botNum, -1.0f ) ) { return false; } botIsBigShot = Client_IsCriticalForCurrentObj( botNum, 3500.0f ); if ( aiState == VLTG && vLTGType == V_DESTROY_DEPLOYABLE ) { deployableInfo_t deployable; if ( GetDeployableInfo( false, vLTGTarget, deployable ) ) { if ( deployable.type == APT || deployable.type == AVT || deployable.type == AIT ) { //mal: these are the priorities isAttackingDeployable = true; } } } for ( i = 0; i < MAX_CLIENTS; i++ ) { if ( !ClientIsValid( i, -1 ) ) { continue; //mal: no valid client in this client slot! } if ( i == botNum ) { continue; //mal: dont try to fight ourselves! } if ( EnemyIsIgnored( i ) ) { continue; //mal: dont try to fight someone we've flagged to ignore for whatever reason! } if ( !Bot_VehicleCanAttackEnemy( i ) ) { //mal: check if the bot has access to a weapon in the vehicle, that can hit this client. continue; } const clientInfo_t& playerInfo = botWorld->clientInfo[ i ]; if ( playerInfo.isNoTarget ) { continue; } //mal: dont target clients that have notarget set - this is useful for debugging, etc. bool enemyIsBigShot = Client_IsCriticalForCurrentObj( i, 2500.0f ); if ( playerInfo.inLimbo ) { continue; } if ( playerInfo.invulnerableEndTime > botWorld->gameLocalInfo.time ) { continue; //mal: ignore revived/just spawned in clients - get the ppl around them! } if ( playerInfo.isActor ) { continue; } if ( playerInfo.health <= 0 ) { continue; } if ( playerInfo.team == botInfo->team ) { continue; } if ( botVehicleInfo->type == MCP && botVehicleInfo->driverEntNum == botNum ) { if ( !InFrontOfVehicle( botInfo->proxyInfo.entNum, playerInfo.origin ) ) { continue; } } if ( botWorld->gameLocalInfo.botSkill == BOT_SKILL_DEMO && botVehicleInfo->type > ICARUS && !playerInfo.isBot ) { //mal: don't attack human players in training mode when we're in flyers. continue; } hasAttackedCriticalMate = ClientHasAttackedTeammate( i, true, 3000 ); hasAttackedMate = ClientHasAttackedTeammate( i, false, 3000 ); hasObj = ClientHasObj( i ); isDefusingOurBomb = ClientIsDefusingOurTeamCharge( i ); inFront = ( botInfo->proxyInfo.weapon == PERSONAL_WEAPON ) ? InFrontOfClient( botNum, playerInfo.origin) : InFrontOfVehicle( botInfo->proxyInfo.entNum, playerInfo.origin ); isFacingUs = ( playerInfo.proxyInfo.entNum == CLIENT_HAS_NO_VEHICLE ) ? InFrontOfClient( i, botInfo->origin ) : InFrontOfVehicle( playerInfo.proxyInfo.entNum, botInfo->origin ); botGotShotRecently = ( botInfo->lastAttackerTime + 3000 < botWorld->gameLocalInfo.time ) ? false : true; isFiringWeapon = playerInfo.weapInfo.isFiringWeap; isNearOurObj = ( LocationDistFromCurrentObj( botInfo->team, playerInfo.origin ) < 2500.0f ) ? true : false; bool isCriticalEnemy = Client_IsCriticalForCurrentObj( i, 2500.0f ); if ( playerInfo.proxyInfo.entNum != CLIENT_HAS_NO_VEHICLE ) { GetVehicleInfo( playerInfo.proxyInfo.entNum, enemyVehicleInfo ); } if ( isAttackingDeployable ) { if ( botInfo->team == botWorld->botGoalInfo.attackingTeam ) { if ( playerInfo.proxyInfo.entNum == CLIENT_HAS_NO_VEHICLE ) { if ( playerInfo.weapInfo.weapon != ROCKET && !isDefusingOurBomb ) { continue; } } else { if ( !( enemyVehicleInfo.flags & ARMOR ) && enemyVehicleInfo.type != ANANSI && enemyVehicleInfo.type != HORNET ) { continue; } } } else { if ( !isDefusingOurBomb && playerInfo.proxyInfo.entNum == CLIENT_HAS_NO_VEHICLE && !hasObj && playerInfo.weapInfo.weapon != ROCKET && ( !isCriticalEnemy || !isNearOurObj ) ) { continue; } } } if ( botIsBigShot && !isFacingUs && ( !botGotShotRecently || botInfo->lastAttacker != i ) && !isFiringWeapon && !hasObj && !isNearOurObj ) { continue; } //mal: if we're trying to do an important obj, dont get into a fight with everyone. vec = playerInfo.origin - botInfo->origin; if ( botVehicleInfo->isAirborneVehicle ) { vec.z = 0.0f; } dist = vec.LengthSqr(); if ( botIsBigShot && !inFront && dist > Square( 2500.0f ) && ( !botGotShotRecently || botInfo->lastAttacker != i ) && botVehicleInfo->driverEntNum == botNum ) { continue; } if ( playerInfo.proxyInfo.entNum != CLIENT_HAS_NO_VEHICLE ) { //mal: pick the driver of a vehicle as the target, NOT passengers, unless there is no driver - then kill whoever. if ( enemyVehicleInfo.type == ANANSI || enemyVehicleInfo.type == HORNET ) { inAttackAirCraft = true; } if ( botIsBigShot && enemyVehicleInfo.type <= ICARUS ) { continue; } if ( enemyVehicleInfo.type == BUFFALO && botVehicleInfo->flags & ARMOR && playerInfo.isBot ) { continue; } if ( enemyVehicleInfo.type == MCP && enemyVehicleInfo.isImmobilized && enemyVehicleInfo.driverEntNum == i ) { continue; } if ( botVehicleInfo->type == ANANSI && enemyVehicleInfo.type == ICARUS ) { //mal: this is funny to watch, but is a waste of time. :-) continue; } if ( isAttackingDeployable ) { if ( botVehicleInfo->isAirborneVehicle && ( enemyVehicleInfo.type <= ICARUS || enemyVehicleInfo.type == BUFFALO ) ) { //mal: if attacking from the air, only worry about air vehicles. continue; } } if ( enemyVehicleInfo.driverEntNum != i && enemyVehicleInfo.driverEntNum != -1 ) { continue; } if ( inAttackAirCraft && enemyVehicleInfo.xyspeed > 500.0f && dist > Square( TANK_MINIGUN_RANGE ) && botVehicleInfo->flags & ARMOR ) { //mal: tanks won't attack fast moving aircraft that are too far away for their MGs continue; } if ( botVehicleInfo->type == BADGER ) { if ( botInfo->proxyInfo.weapon == NULL_VEHICLE_WEAPON && enemyVehicleInfo.flags & ARMOR || enemyVehicleInfo.inWater || ( enemyVehicleInfo.isAirborneVehicle && dist > Square( 3000.0f ) || enemyVehicleInfo.type == HOG ) ) { continue; } } if ( botVehicleInfo->inWater && botInfo->proxyInfo.weapon == NULL_VEHICLE_WEAPON ) { continue; } if ( botVehicleInfo->type != ANANSI && botVehicleInfo->type != HORNET ) { if ( enemyVehicleInfo.xyspeed > 600.0f && dist > Square( 1900.0f ) && !InFrontOfVehicle( enemyVehicleInfo.entNum, botInfo->origin ) && !ClientHasObj( i ) && !enemyIsBigShot ) { //mal: if they're in a mad dash away from us, forget about them! continue; } } } if ( botVehicleInfo->type == BUFFALO && !inAttackAirCraft ) { //mal_TODO: need to make the buffalo a more effective fighting platform! continue; } tempSightDist = botSightDist; if ( !ClientHasObj( i ) && !enemyIsBigShot && playerInfo.proxyInfo.entNum == CLIENT_HAS_NO_VEHICLE && !playerInfo.isCamper && playerInfo.killsSinceSpawn < KILLING_SPREE && botVehicleInfo->driverEntNum == botNum && !isDefusingOurBomb ) { //mal: vehicles will prefer to fight other vehicles, not some guy on foot a mile away.... tempSightDist = Square( 3500.0f ); } if ( inAttackAirCraft && ( botVehicleInfo->type == ANANSI || botVehicleInfo->type == HORNET ) && botWorld->gameLocalInfo.botSkill > BOT_SKILL_EASY ) { tempSightDist = Square( AIRCRAFT_ATTACK_DIST ); } if ( dist > tempSightDist ) { continue; } if ( playerInfo.isDisguised ) { //mal: when in a vehicle, bots are much less likely to notice or worry about coverts if ( botThreadData.GetBotSkill() == BOT_SKILL_EASY ) { continue; } else { if ( ( playerInfo.disguisedClient != botNum && !hasAttackedMate ) || dist > Square( 2500.0f ) ) { continue; } } } if ( !ClientHasObj( i ) ) { audible = ClientIsAudibleToVehicle( i ); //mal: if we can hear you, we'll skip the FOV test in the vis check below } else { audible = true; dist = Square( 500.0f ); //mal: if you've got the docs, your our priority target, unless someone else is right on top of us! } isVisible = ClientIsVisibleToBot( i, !audible, false ); if ( !isVisible && !ClientHasObj( i ) ) { continue; } if ( botWorld->gameLocalInfo.botSkill != BOT_SKILL_DEMO || botWorld->botGoalInfo.gameIsOnFinalObjective || botWorld->botGoalInfo.attackingTeam == botInfo->team ) { if ( isDefusingOurBomb ) { dist = Square( 100.0f ); } if ( hasAttackedCriticalMate && inFront && botWorld->gameLocalInfo.botSkill > BOT_SKILL_EASY ) { dist = Square( 600.0f ); //mal: will give higher priority to someone attacking a critical mate, if we can see it happening. } if ( botWorld->gameLocalInfo.botSkill > BOT_SKILL_EASY ) { if ( Client_IsCriticalForCurrentObj( i, 6000.0f ) ) { dist = Square( 700.0f ); //mal: if your a critical client, we're more likely to kill you. } } if ( botWorld->botGoalInfo.mapHasMCPGoal ) { if ( playerInfo.proxyInfo.entNum == botWorld->botGoalInfo.botGoal_MCP_VehicleNum ) { dist = 400.0f; } } //mal: if your in MCP, you get higher priority then a normal enemy. Especially when we're in a vehicle! if ( botVehicleInfo->type > ICARUS && botVehicleInfo->type != BUFFALO && ( playerInfo.isCamper || playerInfo.killsSinceSpawn >= KILLING_SPREE ) && botWorld->gameLocalInfo.botSkill > BOT_SKILL_EASY && !playerInfo.isBot ) { dist = Square( 600.0f ); } //mal: target trouble making humans! } else { if ( !playerInfo.isBot || playerInfo.isActor ) { dist += Square( TRAINING_MODE_RANGE_ADDITION ); } } numVisEnemies++; if ( dist < entDist ) { entClientNum = i; entDist = dist; } } if ( entClientNum != -1 ) { enemy = entClientNum; enemySpawnID = botWorld->clientInfo[ entClientNum ].spawnID; enemyInfo.enemy_FS_Pos = botWorld->clientInfo[ entClientNum ].origin; enemyInfo.enemy_LS_Pos = enemyInfo.enemy_FS_Pos; bot_FS_Enemy_Pos = botInfo->origin; bot_LS_Enemy_Pos = bot_FS_Enemy_Pos; enemyInfo.enemyLastVisTime = botWorld->gameLocalInfo.time; enemyAcquireTime = botWorld->gameLocalInfo.time; Bot_SetAttackTimeDelay( inFront ); //mal: this sets a delay on how long the bot should take to see enemy, based on bot's state. VEHICLE_COMBAT_AI_SUB_NODE = NULL; //mal: reset the bot's combat AI node COMBAT_MOVEMENT_STATE = NULL; return true; } return false; }
/* ================ idBotAI::COMBAT_Vehicle_EvadeEnemy ================ */ bool idBotAI::COMBAT_Vehicle_EvadeEnemy() { bool bailOut = false; idVec3 vec; if ( !ClientIsValid( enemy, -1 ) ) { Bot_ResetEnemy(); return false; } if ( botVehicleInfo->type == MCP ) { assert( actionNum > -1 ); if ( Bot_CheckActionIsValid( actionNum ) ) { vec = botThreadData.botActions[ actionNum ]->GetActionOrigin() - botVehicleInfo->origin; if ( vec.LengthSqr() > Square( MCP_PARKED_DIST ) ) { Bot_SetupVehicleMove( vec3_zero, -1, actionNum ); if ( MoveIsInvalid() ) { //mal: this should NEVER happen - but if it does, the bot is better off leaving. Bot_ExitVehicleAINode( true ); Bot_ExitVehicle( false ); assert( false ); return false; } Bot_MoveToGoal( botAAS.path.moveGoal, vec3_zero, RUN, NULLMOVETYPE ); } else { Bot_ExitVehicleAINode( true ); //mal: reached our goal, so just leave, and let the MCP deploy! Bot_ExitVehicle( false ); return false; } } } else { if ( AIStack.stackActionNum != ACTION_NULL ) { float evadeDist = 1200.0f; vec = botThreadData.botActions[ AIStack.stackActionNum ]->origin - botInfo->origin; if ( botVehicleInfo->type > ICARUS ) { evadeDist = 550.0f; bailOut = true; vec.z = 0.0f; } if ( vec.LengthSqr() > Square( evadeDist ) ) { Bot_SetupVehicleMove( vec3_zero, -1, AIStack.stackActionNum ); bailOut = false; if ( MoveIsInvalid() ) { VEHICLE_COMBAT_AI_SUB_NODE = &idBotAI::Enter_COMBAT_Vehicle_AttackEnemy; //mal: if theres a problem getting to our target, just fight our enemy normally. return false; } Bot_MoveToGoal( botAAS.path.moveGoal, vec3_zero, RUN, NULLMOVETYPE ); } if ( vec.LengthSqr() < Square( 800.0f ) && botVehicleInfo->type != MCP ) { //mal: get to the outpost if MCP, even to the bitter end! VEHICLE_COMBAT_AI_SUB_NODE = &idBotAI::Enter_COMBAT_Vehicle_AttackEnemy; //mal: we're right at our target, so just fight our enemy normally. return false; } } else { // assert( false ); VEHICLE_COMBAT_AI_SUB_NODE = &idBotAI::Enter_COMBAT_Vehicle_AttackEnemy; return false; } } if ( bailOut ) { Bot_ExitVehicle(); return false; } Bot_PickBestVehicleWeapon(); if ( !enemyInfo.enemyVisible && enemyInfo.enemyLastVisTime + 500 < botWorld->gameLocalInfo.time ) { UpdateNonVisEnemyInfo(); if ( BotLeftEnemysSight() ) { vec = bot_LS_Enemy_Pos; } else { vec = enemyInfo.enemy_LS_Pos; } Bot_LookAtLocation( vec, AIM_TURN ); } else { if ( botVehicleInfo->type > ICARUS ) { vec = botWorld->clientInfo[ enemy ].origin - botVehicleInfo->origin; vec[ 2 ] = 0.0f; if ( vec.LengthSqr() > Square( 2500.0f ) && InFrontOfVehicle( botVehicleInfo->entNum, botWorld->clientInfo[ enemy ].origin ) && botVehicleInfo->type != BUFFALO ) { Bot_LookAtEntity( enemy, AIM_TURN ); Bot_CheckVehicleAttack(); } else { Bot_LookAtLocation( botAAS.path.viewGoal, SMOOTH_TURN ); if ( botVehicleInfo->type != BUFFALO ) { Bot_CheckVehicleAttack(); } } } else { Bot_LookAtEntity( enemy, AIM_TURN ); Bot_CheckVehicleAttack(); } } return true; }