// BotAI_Battle_Fight.cpp // #pragma hdrstop #include "precompiled.h" #include "../Game_local.h" /* ===================== rvmBot::state_BattleFight ===================== */ stateResult_t rvmBot::state_BattleFight(stateParms_t* parms) { idVec3 target; idPlayer* entinfo; // respawn if dead. if( BotIsDead( &bs ) ) { stateThread.SetState("state_Respawn"); return SRESULT_DONE_FRAME; } //if there is another better enemy if( BotFindEnemy( &bs, bs.enemy ) ) { common->DPrintf( "found new better enemy\n" ); } //if no enemy if( bs.enemy < 0 || bs.enemy >= gameLocal.num_entities || gameLocal.entities[bs.enemy] == NULL ) { stateThread.SetState("state_SeekLTG"); return SRESULT_DONE_FRAME; } entinfo = gameLocal.entities[bs.enemy]->Cast(); if( entinfo == NULL ) { stateThread.SetState("state_SeekLTG"); return SRESULT_DONE_FRAME; } //if the enemy is dead if( bs.enemydeath_time ) { if( bs.enemydeath_time < Bot_Time() - 1.0 ) { bs.enemydeath_time = 0; bs.ltg_time = 0; stateThread.SetState("state_SeekLTG"); return SRESULT_DONE_FRAME; } } else if( EntityIsDead( entinfo ) ) { bs.enemydeath_time = Bot_Time(); } target = entinfo->GetOrigin(); //update the attack inventory values BotUpdateBattleInventory( &bs, bs.enemy ); //if the enemy is not visible if( !BotEntityVisible( bs.entitynum, bs.eye, bs.viewangles, 360, bs.enemy ) ) { if( BotWantsToChase( &bs ) ) { bs.chase_time = Bot_Time() + 10.0f; stateThread.SetState("state_Chase"); return SRESULT_DONE_FRAME; } else { stateThread.SetState("state_SeekLTG"); return SRESULT_DONE_FRAME; } } bs.enemyvisible_time = Bot_Time(); bs.lastenemyorigin = target; bs.last_enemy_visible_position = target; //use holdable items BotBattleUseItems( &bs ); //choose the best weapon to fight with BotChooseWeapon( &bs ); // Opportunistic combat pickup: keep fighting, but route through a nearby item // instead of throwing the current goal away and random-strafing forever. The // NBG selector itself expands this range when the bot is stuck on pistol/dry // ammo, and otherwise keeps the detour short. if( bs.check_time < Bot_Time() ) { bs.check_time = Bot_Time() + 0.45f; bot_goal_t pickupAnchor; pickupAnchor.Reset(); pickupAnchor.entitynum = bs.enemy; pickupAnchor.origin = target; pickupAnchor.mins = idVec3( -8.0f, -8.0f, -8.0f ); pickupAnchor.maxs = idVec3( 8.0f, 8.0f, 8.0f ); const float pickupRange = 260.0f; if( BotNearbyGoal( &bs, 0, &pickupAnchor, pickupRange ) ) { bs.nbg_time = Bot_Time() + 18.0f; stateThread.SetState("state_BattleNBG"); return SRESULT_DONE_FRAME; } } // Fight like a deathmatch bot: keep pressure on the enemy while changing // strafe direction often enough to be hard to track. if( bs.attackstrafe_time < Bot_Time() ) { bs.attackstrafe_time = Bot_Time() + idMath::FRandRange( 0.65f, 1.35f ); if( rvmBotUtil::random() < 0.5f ) { bs.flags ^= BFL_STRAFERIGHT; } } idVec3 toEnemy = target - bs.origin; toEnemy[2] = 0.0f; const float enemyDist = toEnemy.Length(); if( enemyDist > 1.0f ) { toEnemy /= enemyDist; idVec3 strafeDir = ( bs.flags & BFL_STRAFERIGHT ) ? idVec3( -toEnemy[1], toEnemy[0], 0.0f ) : idVec3( toEnemy[1], -toEnemy[0], 0.0f ); idVec3 moveDir = strafeDir; if( enemyDist > 520.0f ) { moveDir += toEnemy * 0.65f; } else if( enemyDist < 170.0f ) { moveDir -= toEnemy * 0.75f; } moveDir.Normalize(); bs.botinput.dir = moveDir; bs.botinput.speed = pm_runspeed.GetInteger(); if( enemyDist < 420.0f && rvmBotUtil::random() < 0.025f ) { bs.botinput.actionflags |= ACTION_JUMP; } } else { BotMoveInRandomDirection(&bs); } //aim at the enemy BotAimAtEnemy( &bs ); //attack the enemy if possible BotCheckAttack( &bs ); //if the bot wants to retreat if( !( bs.flags & BFL_FIGHTSUICIDAL ) ) { if( BotWantsToRetreat( &bs ) ) { stateThread.SetState("state_Retreat"); return SRESULT_DONE_FRAME; } } return SRESULT_WAIT; }