diff options
Diffstat (limited to 'src/parsec/g_bot_cl.cpp')
| -rw-r--r-- | src/parsec/g_bot_cl.cpp | 1028 |
1 files changed, 1028 insertions, 0 deletions
diff --git a/src/parsec/g_bot_cl.cpp b/src/parsec/g_bot_cl.cpp new file mode 100644 index 0000000..0e364a2 --- /dev/null +++ b/src/parsec/g_bot_cl.cpp @@ -0,0 +1,1028 @@ +/* + * PARSEC - + * + * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:36 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2002 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +// C library +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "objstruc.h" + +// global externals +#include "globals.h" + +// subsystem headers +//#include "aud_defs.h" +#include "inp_defs.h" +#include "net_defs.h" +#include "sys_defs.h" +//#include "vid_defs.h" + +// mathematics header +#include "utl_math.h" + +// proprietary headers +#include "con_arg.h" +#include "con_int.h" +#include "con_com.h" +#include "con_main.h" +#include "e_callbk.h" +#include "inp_user.h" +#include "m_main.h" +#include "net_conn.h" +#include "obj_ctrl.h" +#include "utl_logfile.h" +#include "g_bot_cl.h" +#include "obj_game.h" +#include "net_glob.h" +#include "od_class.h" + +static int botwait = 0; + +// do the desired object control ---------------------------------------------- +// +int OCT_DoControl( object_control_s* objctl ) +{ + ASSERT( objctl != NULL ); + ASSERT( ( objctl->rot_x >= -1.0f ) && ( objctl->rot_x <= 1.0f ) ); + ASSERT( ( objctl->rot_y >= -1.0f ) && ( objctl->rot_y <= 1.0f ) ); + ASSERT( ( objctl->rot_z >= -1.0f ) && ( objctl->rot_z <= 1.0f ) ); + ASSERT( ( objctl->accel >= -1.0f ) && ( objctl->accel <= 1.0f ) ); + + // check whether we have a valid object to control + if ( objctl->pShip == NULL ) { + CON_AddMessage( "OCT: not object to control is set." ); + return FALSE; + } + + //FIXME: for now we only support ship objects + if ( !OBJECT_TYPE_SHIP( objctl->pShip ) ) { + CON_AddMessage( "OCT: controlled object is not a ship." ); + return FALSE; + } + + int ship_controlled = FALSE; + + // check for rotation around x axis - PITCH - ( -1 = pullup, +1 = divedown ) + if ( objctl->rot_x != 0.0f ) { + bams_t _angle = (bams_t)(objctl->rot_x * objctl->pShip->PitchPerRefFrame * CurScreenRefFrames); + //ObjRotX( objctl->pShip->ObjPosition, _angle ); + INP_UserRotX( _angle ); + ship_controlled = TRUE; + } + + // check for rotation around y axis - YAW - ( -1 = right, +1 = left ) + if ( objctl->rot_y != 0.0f ) { + bams_t _angle = (bams_t)(objctl->rot_y * objctl->pShip->YawPerRefFrame * CurScreenRefFrames); + //ObjRotY( objctl->pShip->ObjPosition, _angle ); + INP_UserRotY( _angle ); + ship_controlled = TRUE; + } + + // check for rotation around z axis - ROLL - ( -1 = right, +1 = left ) + if ( objctl->rot_z != 0.0f ) { + bams_t _angle = (bams_t)(objctl->rot_z * objctl->pShip->RollPerRefFrame * CurScreenRefFrames); + //ObjRotZ( objctl->pShip->ObjPosition, _angle ); + INP_UserRotZ( _angle ); + ship_controlled = TRUE; + } + + // check for accel control ( +1 accelerate, -1 decelerate ) + if ( objctl->accel != 0.0f ) { + //fixed_t _accel = FLOAT_TO_FIXED( objctl->accel * (float)(objctl->pShip->SpeedIncPerRefFrame) * CurScreenRefFrames ); + fixed_t _accel = (fixed_t)(objctl->accel * objctl->pShip->SpeedIncPerRefFrame * CurScreenRefFrames); + INP_UserAcceleration( _accel ); + +/* + // alter accel + objctl->pShip->CurSpeed += c_speed; + + // check against speed constraints + if ( objctl->pShip->CurSpeed > objctl->pShip->MaxSpeed ) { + objctl->pShip->CurSpeed = objctl->pShip->MaxSpeed; + } else if ( objctl->pShip->CurSpeed < 0 ) { + objctl->pShip->CurSpeed = 0; + } +*/ + + ship_controlled = TRUE; + } + + //// we always move the controlled ship according to the speed + //if ( objctl->pShip->CurSpeed != 0 ) { + // + // // actually move the ship + // Vector3 dirvec; + // fixed_t speed = objctl->pShip->CurSpeed * CurScreenRefFrames; + // DirVctMUL( objctl->pShip->ObjPosition, FIXED_TO_GEOMV( speed ), &dirvec ); + // objctl->pShip->ObjPosition[ 0 ][ 3 ] += dirvec.X; + // objctl->pShip->ObjPosition[ 1 ][ 3 ] += dirvec.Y; + // objctl->pShip->ObjPosition[ 2 ][ 3 ] += dirvec.Z; + //} + + return ship_controlled; +} + +// dump an object control to the console -------------------------------------- +// +void OCT_Dump(object_control_s* objctl ) +{ + ASSERT( objctl != NULL ); + ASSERT( objctl->pShip != NULL ); + + char szBuffer[ 512 ]; + sprintf( szBuffer, "OCT: ObjID: %03u rotx: %02u roty: %02u rotz: %02u accel: %02u\n", + (unsigned int)objctl->pShip->ObjectNumber, + (unsigned int)objctl->rot_x, + (unsigned int)objctl->rot_y, + (unsigned int)objctl->rot_z, + (unsigned int)objctl->accel ); + + CON_AddMessage( szBuffer ); +} + +// **************************************************************************** +// **************************************************************************** +// **************************************************************************** + +// bot logfile ---------------------------------------------------------------- +// +#ifdef BOT_LOGFILES + +static UTL_LogFile g_BotLog( "bot.log" ); +static UTL_LogFile g_BotPosLog( "bot_pos.log" ); +static UTL_LogFile g_BotXDirLog( "bot_xdir.log" ); +static UTL_LogFile g_BotZDirLog( "bot_zdir.log" ); +static UTL_LogFile g_BotAccelModeLog( "bot_accel.log" ); + +#define BOT_MsgOut g_BotLog.printf +#define BOT_Pos_MsgOut g_BotPosLog.printf +#define BOT_XDir_MsgOut g_BotXDirLog.printf +#define BOT_ZDir_MsgOut g_BotZDirLog.printf +#define BOT_AccelMode_MsgOut g_BotAccelModeLog.printf + +#endif // !BOT_LOGFILES + +// ---------------------------------------------------------------------------- +// +void UTL_LocomotionController::ControlOjbect( object_control_s* pObjctl, Vector3* pDesiredVelocity, fixed_t _DesiredSpeed ) +{ + ASSERT( pObjctl != NULL ); + ASSERT( pDesiredVelocity != NULL ); + + Vector3 xDir, yDir, zDir; + FetchXVector( pObjctl->pShip->ObjPosition, &xDir ); + FetchYVector( pObjctl->pShip->ObjPosition, &yDir ); + FetchZVector( pObjctl->pShip->ObjPosition, &zDir ); + + geomv_t len = VctLenX( pDesiredVelocity ); + + // stop control if desired velocity is zero + if ( len <= GEOMV_VANISHING ) { + pObjctl->rot_x = 0; + pObjctl->rot_y = 0; + pObjctl->accel = -0.82; + +#ifdef BOT_LOGFILES + BOT_MsgOut( "ControlOjbect() got dimishing desired velocity" ); +#endif // BOT_LOGFILES + + return; + } else { + Vector3 DesVelNorm; + DesVelNorm.X = FLOAT_TO_GEOMV( pDesiredVelocity->X / len ); + DesVelNorm.Y = FLOAT_TO_GEOMV( pDesiredVelocity->Y / len ); + DesVelNorm.Z = FLOAT_TO_GEOMV( pDesiredVelocity->Z / len ); + + geomv_t yaw_dot = DOT_PRODUCT( &DesVelNorm, &xDir ); + geomv_t pitch_dot = DOT_PRODUCT( &DesVelNorm, &yDir ); + geomv_t heading_dot = DOT_PRODUCT( &DesVelNorm, &zDir ); + + float fDesiredSpeed = FIXED_TO_FLOAT( _DesiredSpeed ); + float fCurSpeed = FIXED_TO_FLOAT( pObjctl->pShip->CurSpeed ); + + float pitch = 0; + float yaw = 0; + + // target is behind us + sincosval_s fullturn; + GetSinCos( DEG_TO_BAMS( 2 * m_nRelaxedHeadingAngle ), &fullturn ); + //if ( heading_dot < 0.0f ) { + if ( heading_dot < -fullturn.cosval ) { + + // we must initiate a turn, if not already in a turn + if ( !pObjctl->IsYaw() || !pObjctl->IsPitch() ) { + + // default to random + yaw = (float)( RAND() % 3 ) - 1; + pitch = (float)( RAND() % 3 ) - 1; + + // steer towards goal + if ( yaw_dot < -GEOMV_VANISHING ) { + yaw = OCT_YAW_LEFT; + } else if ( yaw_dot > GEOMV_VANISHING ) { + yaw = OCT_YAW_RIGHT; + } + if ( pitch_dot < -GEOMV_VANISHING ) { + pitch = OCT_PITCH_UP; + } else if ( pitch_dot > GEOMV_VANISHING ) { + pitch = OCT_PITCH_DOWN; + } + + } else { + // reuse prev. turn information + pitch = pObjctl->rot_x; + yaw = pObjctl->rot_y; + } + + // slow down, until in direction of target + // pObjctl->accel = heading_dot; + //pObjctl->accel = OCT_DECELERATE; + + // also maintain min. speed during turns + if ( fCurSpeed > m_fMinSpeedTurn ) { + pObjctl->accel = -0.42; + } + + } else { + + // determine accel + if ( fDesiredSpeed > fCurSpeed ) { + // accelerate towards target + pObjctl->accel = OCT_ACCELERATE; + } else if ( fDesiredSpeed < fCurSpeed ) { + // decelerate towards target + pObjctl->accel = OCT_DECELERATE; + } else { + // no accel + pObjctl->accel = 0; + } + + // heading must be inside of 5 degrees cone angle + sincosval_s sincosv; + GetSinCos( DEG_TO_BAMS( m_nRelaxedHeadingAngle ), &sincosv ); + if ( yaw_dot < -sincosv.sinval ) { + yaw = OCT_YAW_LEFT; + } else if ( yaw_dot > sincosv.sinval ) { + yaw = OCT_YAW_RIGHT; + } + if ( pitch_dot < -sincosv.sinval ) { + pitch = OCT_PITCH_UP; + } else if ( pitch_dot > sincosv.sinval ) { + pitch = OCT_PITCH_DOWN; + } + + // if heading outside of 30 degrees cone angle, we decelerate + GetSinCos( DEG_TO_BAMS( m_nFullSpeedHeading ), &sincosv ); + bool_t bYawOutsideHeading = ( ( yaw_dot < -sincosv.sinval ) || ( yaw_dot > sincosv.sinval ) ); + bool_t bPitchOutsideHeading = ( ( pitch_dot < -sincosv.sinval ) || ( pitch_dot > sincosv.sinval ) ); + if ( bYawOutsideHeading || bPitchOutsideHeading ) { + + // also maintain min. speed during turns + if ( fCurSpeed > min( m_fMinSpeedTurn, fDesiredSpeed ) ) { + // decelerate towards target + pObjctl->accel = OCT_DECELERATE; + } + } + } + +#ifdef BOT_LOGFILES + BOT_MsgOut( "heading_dot: %f, yaw_dot: %f, pitch_dot: %f", heading_dot, yaw_dot, pitch_dot ); +#endif // BOT_LOGFILES + + //FIXME: oct must also handle slide horiz./vert. + + pObjctl->rot_x = pitch; + pObjctl->rot_y = yaw; + } +} + +//---------------------------------------------------------------------------- + +static int clbot_char_plan_freq = 1; +static int clbot_char_goal_freq = 10; +static int clbot_char_input_freq = 10; + +PRIVATE +void RealizeBotChar() +{ + TheBot->GetCharacter()->m_fPlanInterval = 1.0f / (float)clbot_char_plan_freq; + TheBot->GetCharacter()->m_fGoalCheckInterval = 1.0f / (float)clbot_char_goal_freq; + TheBot->GetCharacter()->m_fInputChangeInterval = 1.0f / (float)clbot_char_input_freq; +} + +// ---------------------------------------------------------------------------- +PRIVATE int FetchBotChar_PlanFreq() { return (int)( ( 1.0f / TheBot->GetCharacter()->GetPlanInterval_sec() ) + 0.5f ); } +PRIVATE int FetchBotChar_GoalFreq() { return (int)( ( 1.0f / TheBot->GetCharacter()->GetGoalCheckInterval_sec() ) + 0.5f ); } +PRIVATE int FetchBotChar_InputFreq() { return (int)( ( 1.0f / TheBot->GetCharacter()->GetInputChangeInterval_sec() ) + 0.5f ); } + +// registration table for bot character variables ----------------------------- +// +int_command_s botchar_int_commands[] = { + { 0x00, "clbot.char.plan_freq", 1, 100, &clbot_char_plan_freq, RealizeBotChar, FetchBotChar_PlanFreq, 0 }, + { 0x00, "clbot.char.goal_freq", 1, 100, &clbot_char_goal_freq, RealizeBotChar, FetchBotChar_GoalFreq, 0 }, + { 0x00, "clbot.char.input_freq", 1, 100, &clbot_char_input_freq, RealizeBotChar, FetchBotChar_InputFreq, 0 }, + + { 0x00, NULL, 0,0,NULL, NULL, NULL, 0 }, +}; + + +// ctor ----------------------------------------------------------------------- +// +BOT_Character::BOT_Character() +{ + Reset(); + + // register commands + for( int_command_s* cmd = botchar_int_commands; cmd->command != NULL; cmd++ ) { + CON_RegisterIntCommand( cmd ); + } +} + +// reset to defaults ---------------------------------------------------------- +// +void BOT_Character::Reset() +{ + m_fPlanInterval = 1.0f; + m_fGoalCheckInterval = 0.1f; // goal checking at 10Hz + m_fInputChangeInterval = 0.1f; // input at 10Hz +} + +// select the attack target --------------------------------------------------- +// +ShipObject* BOT_Character::SelectAttackTarget( ShipObject* pAttacker ) +{ + ShipObject* pCurTarget = NULL; + int nCurTargetDamageRemain = 0; + + for ( ShipObject* pShip = /*TheWorld->*/FetchFirstShip(); pShip != NULL; ) { + + // do not select self as target + if ( pAttacker == pCurTarget ) + continue; + + if ( pCurTarget == NULL ) { + pCurTarget = pShip; + nCurTargetDamageRemain = ( pCurTarget->MaxDamage - pCurTarget->CurDamage ); + } else { + // select weakest ship as target + int nDamageRemain = ( pShip->MaxDamage - pShip->CurDamage ); + + if ( nDamageRemain > nCurTargetDamageRemain ) { + pCurTarget = pShip; + nCurTargetDamageRemain = nDamageRemain; + } + } + + pShip = (ShipObject *) pShip->NextObj; + } + + return pCurTarget; +} + + +// main think function, called every render or sim frame ( client or server ) +// +void BOT_AI::DoThink() +{ + ASSERT( NetJoined ); + // count down fire disable delay + // get the agent position for this frame + FetchTVector( m_pShip->ObjPosition, &m_AgentPos ); + // count down fire disable delay + if ( FireDisable > 0 ) { + FireDisable -= CurScreenRefFrames; + } + + // do planning ? + if ( m_PlanTimeout.IsTimeout() ) { + _DoPlan(); + } + + // do goal checking ? + if ( m_GoalCheckTimeout.IsTimeout() ) { + // check goals for completion and define new goals + switch ( m_nAgentMode ) { + case AGENTMODE_IDLE: + _GoalCheck_AgentMode_Idle(); + break; + case AGENTMODE_POWERUP: + _GoalCheck_AgentMode_Powerup(); + break; + case AGENTMODE_ATTACK: + _GoalCheck_AgentMode_Attack(); + break; + case AGENTMODE_RETREAT: + _GoalCheck_AgentMode_Retreat(); + break; + default: + ASSERT( FALSE ); + return; + } + } + + // change steering ? + if ( m_InputTimeout.IsTimeout() ) { + + // modify ObjectControl to steer to position in current goal + _SteerToPosition( m_State.GetCurGoal()->GetGoalPosition(), m_State.GetObjectControl() ); + } + + // actually control the object + OCT_DoControl( m_State.GetObjectControl() ); +} + + +// determine in which agentmode we should be ---------------------------------- +// +void BOT_AI::_DoPlan() +{ + ShipObject* pTargetObject; + BOT_Goal* pGoal = m_State.GetCurGoal(); + + if(m_pShip->CurDamage > (m_pShip->MaxDamage * 0.9)) { //90% damage + m_nAgentMode = AGENTMODE_RETREAT; + return; + } + if(m_pShip->CurEnergy < (m_pShip->MaxEnergy * 0.1)) { //10% energy left + m_nAgentMode = AGENTMODE_RETREAT; + return; + } + if(NumRemPlayers > 1) + pTargetObject = m_Character.SelectAttackTarget( m_pShip ); //Check for target + else + pTargetObject = NULL; + // no target + if ( pTargetObject == NULL ) { + ExtraObject* pObject = FetchFirstExtra(); //Check for powerups + if(pObject != NULL) { + m_nAgentMode = AGENTMODE_POWERUP; + + } else { + m_nAgentMode = AGENTMODE_IDLE; + } + } else { + m_nAgentMode = AGENTMODE_ATTACK; + } +} + + +// ---------------------------------------------------------------------------- +// +void BOT_AI::_GoalCheck_AgentMode_Idle() +{ + + + +#ifdef BOT_LOGFILES + BOT_MsgOut( "new goal position: %f %f %f", pGoalPos->X, pGoalPos->Y, pGoalPos->Z ); +#endif // BOT_LOGFILES +// } +} + + +// ---------------------------------------------------------------------------- +// +void BOT_AI::_GoalCheck_AgentMode_Powerup() +{ + BOT_Goal* pGoal = m_State.GetCurGoal(); + Vector3* pGoalPos = pGoal->GetGoalPosition(); + + ASSERT( pGoal != NULL ); + if(pGoal->GetTargetObject() == NULL) { + ExtraObject* pObject = FetchFirstExtra(); + if(pObject == NULL) { + m_nAgentMode = AGENTMODE_IDLE; + return; + } + pGoal->SetTargetObject(pObject); + FetchTVector( pObject->ObjPosition, pGoalPos ); +#ifdef BOT_LOGFILES + BOT_MsgOut("Targetting Extra"); +#endif + } + + pGoalPos = pGoal->GetGoalPosition(); + + // get vector to target + Vector3 vec2Target; + VECSUB( &vec2Target, pGoalPos, &m_AgentPos ); + + float len = VctLenX( &vec2Target ); +#ifdef BOT_LOGFILES + BOT_MsgOut( "BOT: distance to goal: %f", len ); +#endif + if ( len < 100.0f ) { +#ifdef BOT_LOGFILES + BOT_MsgOut("Clearing Goal"); + pGoal->SetTargetObject(NULL); +#endif + } + +} + + + +// set the currently used goal position --------------------------------------- +// +void BOT_AI::_GoalCheck_AgentMode_Attack() +{ + //FIXME: push current goal onto goal stack + BOT_Goal* pGoal = m_State.GetCurGoal(); + ASSERT( pGoal != NULL ); + + //FIXME: here we should also evaluate whether we pick another target + + // select a target, if none, or no ship selected as target + GenObject* pTargetObject = pGoal->GetTargetObject(); + Vector3* pGoalPos = pGoal->GetGoalPosition(); + if( ( pTargetObject == NULL ) || ( OBJECT_TYPE_SHIP( pTargetObject) == FALSE ) ){ + + // let the character select a target + pTargetObject = m_Character.SelectAttackTarget( m_pShip ); + + // no target + if ( pTargetObject == NULL ) { + //FIXME: transition function + m_nAgentMode = AGENTMODE_IDLE; + pGoal->SetTargetObject(NULL); + +#ifdef BOT_LOGFILES + BOT_MsgOut( "switching from ATTACK to IDLE" ); +#endif // BOT_LOGFILES + + return; + } + + // set the target object + pGoal->SetTargetObject( pTargetObject ); + + // set the goal position to where the target is + FetchTVector( pTargetObject->ObjPosition, pGoalPos ); + +#ifdef BOT_LOGFILES + BOT_MsgOut( "found new ATTACK target %d", pTargetObject->HostObjNumber ); +#endif // BOT_LOGFILES + + } + + Vector3 TargetPos; + FetchTVector( pTargetObject->ObjPosition, &TargetPos ); + + // get vector to target + Vector3 vec2Target; + VECSUB( &vec2Target, &TargetPos, &m_AgentPos ); + + float len = VctLenX( &vec2Target ); +#ifdef BOT_LOGFILES + BOT_MsgOut( "BOT: distance to target: %f", len ); +#endif +#define MIN_DISTANCE_TO_TARGET 100.0f + if ( len < 500.0F) { + if ( FireRepeat > 0 ) { + FireRepeat -= CurScreenRefFrames; + } + if ( ( FireRepeat > 0 ) || ( FireDisable > 0 ) ) { + //do nothing! + } else { + + // create laser + OBJ_ShootLaser( m_pShip ); + + if ( ( FireRepeat += MyShip->FireRepeatDelay ) <= 0 ) { + FireRepeat = 1; + } + if ( ( FireDisable += MyShip->FireDisableDelay ) <= 0 ) { + FireDisable = 1; + } + } + } + if ( len < MIN_DISTANCE_TO_TARGET ) { + + // if nearby goal, we stay where we are + memcpy( pGoalPos, &m_AgentPos, sizeof( Vector3 ) ); + + } else { + + // set the goal position to the position of the target + ASSERT( pTargetObject != NULL ); + FetchTVector( pTargetObject->ObjPosition, pGoalPos ); + } + +#ifdef BOT_LOGFILES + BOT_MsgOut( "ATTACK goal is at %f %f %f", pGoalPos->X, pGoalPos->Y, pGoalPos->Z ); +#endif // BOT_LOGFILES +} + +// search for the repair module --------------------------------------------------- +// +ExtraObject* BOT_Character::SelectRepairObject() +{ + ExtraObject* pCurTarget = NULL; + + for ( ExtraObject* pSearch = FetchFirstExtra(); pSearch != NULL; ) { + + if ( pCurTarget == NULL ) { //Save what we find first, if it matches great if not, search on + pCurTarget = pSearch; + if(pCurTarget->ObjectType == EXTRA1TYPE) { + Extra1Obj *extra1po = (Extra1Obj *) pCurTarget; + if( extra1po->ObjectClass == REPAIR_EXTRA_CLASS) + return pCurTarget; //found it first try! + } + } else { + //Find Repair module + if(pSearch->ObjectType == EXTRA1TYPE) { + Extra1Obj *extra1po = (Extra1Obj *) pSearch; + if( extra1po->ObjectClass == REPAIR_EXTRA_CLASS) + return pSearch; //found it first try! + } + } + pSearch = (ExtraObject *) pSearch->NextObj; + } + return pCurTarget; //Return Original or NULL search item as we couldnt find the rep module +} + +// search for the energy module --------------------------------------------------- +// +ExtraObject* BOT_Character::SelectEnergyObject() +{ + ExtraObject* pCurTarget = NULL; + + for ( ExtraObject* pSearch = FetchFirstExtra(); pSearch != NULL; ) { + + if ( pCurTarget == NULL ) { //Save what we find first, if it matches great if not, search on + pCurTarget = pSearch; + if(pCurTarget->ObjectType == EXTRA1TYPE) { + Extra1Obj *extra1po = (Extra1Obj *) pCurTarget; + if( extra1po->ObjectClass == ENERGY_EXTRA_CLASS) + return pCurTarget; //found it first try! + } + } else { + //Find Repair module + if(pSearch->ObjectType == EXTRA1TYPE) { + Extra1Obj *extra1po = (Extra1Obj *) pSearch; + if( extra1po->ObjectClass == ENERGY_EXTRA_CLASS) + return pSearch; //found it first try! + } + } + pSearch = (ExtraObject *) pSearch->NextObj; + } + return pCurTarget; //Return Original or NULL search item as we couldnt find the energy module +} + +// ---------------------------------------------------------------------------- +// +void BOT_AI::_GoalCheck_AgentMode_Retreat() +{ + BOT_Goal* pGoal = m_State.GetCurGoal(); + Vector3* pGoalPos = pGoal->GetGoalPosition(); + ExtraObject* pObject = NULL; + ASSERT( pGoal != NULL ); + + if(pGoal->GetTargetObject() == NULL) { + if(m_pShip->CurDamage > (m_pShip->MaxDamage * 0.9)) { + pObject = m_Character.SelectRepairObject(); + } + if(m_pShip->CurEnergy < (m_pShip->MaxEnergy * 0.1)) { + pObject = m_Character.SelectEnergyObject(); + m_nAgentMode = AGENTMODE_RETREAT; + } + if(pObject == NULL) { + m_nAgentMode = AGENTMODE_IDLE; + return; + } + pGoal->SetTargetObject(pObject); + FetchTVector( pObject->ObjPosition, pGoalPos ); +#ifdef BOT_LOGFILES + BOT_MsgOut("Retreat: Targetting Extra"); +#endif + } + + pGoalPos = pGoal->GetGoalPosition(); + + // get vector to target + Vector3 vec2Target; + VECSUB( &vec2Target, pGoalPos, &m_AgentPos ); + + float len = VctLenX( &vec2Target ); +#ifdef BOT_LOGFILES + BOT_MsgOut( "BOT: distance to goal: %f", len ); +#endif + if ( len < 100.0f ) { +#ifdef BOT_LOGFILES + BOT_MsgOut("Clearing Goal"); +#endif + pGoal->SetTargetObject(NULL); + + } + +} + + +// ---------------------------------------------------------------------------- +// +void BOT_AI::_SteerToPosition( Vector3* targetPos, object_control_s* pObjctl ) +{ + // get vector to target + Vector3 vec2Target; + VECSUB( &vec2Target, targetPos, &m_AgentPos ); + + float oldaccel = pObjctl->accel; + + UTL_LocomotionController _LomoCtrl; + _LomoCtrl.ControlOjbect( pObjctl, &vec2Target, pObjctl->pShip->MaxSpeed ); + + if ( pObjctl->accel != oldaccel ) { +#ifdef BOT_LOGFILES + BOT_AccelMode_MsgOut( "%f %f %f", m_AgentPos.X, m_AgentPos.Y, m_AgentPos.Z ); +#endif // BOT_LOGFILES + } + + Vector3 xDir, yDir, zDir; + FetchXVector( m_pShip->ObjPosition, &xDir ); + FetchYVector( m_pShip->ObjPosition, &yDir ); + FetchZVector( m_pShip->ObjPosition, &zDir ); + +#ifdef BOT_LOGFILES + BOT_MsgOut( "pos: %7.2f/%7.2f/%7.2f vec2target: %7.2f/%7.2f/%7.2f xDir: %7.2f/%7.2f/%7.2f yDir: %7.2f/%7.2f/%7.2f rot_x: %7.2f rot_y: %7.2f accel: %7.2f", + m_AgentPos.X, m_AgentPos.Y, m_AgentPos.Z, + vec2Target.X, vec2Target.Y, vec2Target.Z, + xDir.X, xDir.Y, xDir.Z, + yDir.X, yDir.Y, yDir.Z, + pObjctl->rot_x, pObjctl->rot_y, pObjctl->accel ); + + BOT_Pos_MsgOut ( "%f %f %f", m_AgentPos.X, m_AgentPos.Y, m_AgentPos.Z ); + BOT_XDir_MsgOut( "%f %f %f", xDir.X, xDir.Y, xDir.Z ); + BOT_ZDir_MsgOut( "%f %f %f", zDir.X, zDir.Y, zDir.Z ); +#endif // BOT_LOGFILES + +} + + + +// check whether current status (connected/joined etc. ) matches the desired one +// +void BOT_ClientSide::_CheckStatus() +{ + BOT_Goal* pGoal = m_State.GetCurGoal(); + // check whether to connect + if ( ( NetConnected == NETWORK_GAME_OFF ) && m_bWantToBeConnected ) { + pGoal->SetTargetObject(NULL); + if ( strlen( m_szServer ) > 0 ) { + // connect + NET_CommandConnect( m_szServer ); + + // set the timeout for the next connection attempt + if ( !NetConnected ) { + m_NextStatusCheckRefFrame = SYSs_GetRefFrameCount() + ( 5 * BOT_STATUS_CHECK_TIMEOUT ); + } + + return; + } else { + +#ifdef BOT_LOGFILES + BOT_MsgOut( "Error: BOT_ClientSide::_CheckStatus(): missing servername for connect !" ); +#endif // BOT_LOGFILES + + } + + // check whether to disconnect + } else if ( ( NetConnected == NETWORK_GAME_ON ) && !m_bWantToBeConnected ) { + + // disconnect + NET_CommandDisconnect(); + return; + } + + if ( NetConnected ) { + // check for join + if ( !NetJoined && m_bWantToBeJoined ) { + if(botwait == 1) { + botwait = 0; + } else { + botwait = 1; + } + // join + pGoal->SetTargetObject(NULL); + if(botwait == 0) { + MENU_FloatingMenuEnterGame(); + } + + if(!NetJoined) { + m_NextStatusCheckRefFrame = SYSs_GetRefFrameCount() + ( 5 * BOT_STATUS_CHECK_TIMEOUT ); + } + return; + + // check for unjoin + } else if ( NetJoined && !m_bWantToBeJoined ) { + + // unjoin + pGoal->SetTargetObject(NULL); + ExitGameLoop = 1; + } + } +} + + +// overwritten think method to allow special client-side things --------------- +// +void BOT_ClientSide::DoThink() +{ + // do status checking ? + if ( SYSs_GetRefFrameCount() >= m_NextStatusCheckRefFrame ) { + m_NextStatusCheckRefFrame = SYSs_GetRefFrameCount() + BOT_STATUS_CHECK_TIMEOUT; + _CheckStatus(); + } + + // AI only active if connected & joined & we have the full state received from the server + if ( NetConnected && NetJoined & HaveFullPlayerState ) { + BOT_AI::DoThink(); + } +} + +// start the bot -------------------------------------------------------------- +// +void BOT_ClientSide::Start() +{ + if ( !m_Started ) { + CALLBACK_RegisterCallback( CBTYPE_USER_INPUT | CBFLAG_PERSISTENT, BOT_CallThink, (void *) this ); + m_Started = TRUE; + +#ifdef BOT_LOGFILES + BOT_MsgOut( "STARTED" ); +#endif // BOT_LOGFILES + } +} + +// stop the bot --------------------------------------------------------------- +// +void BOT_ClientSide::Stop() +{ + if ( m_Started ) { + int numremoved = CALLBACK_DestroyCallback( CBTYPE_USER_INPUT | CBFLAG_PERSISTENT, (void *) this ); + ASSERT( numremoved <= 1 ); + m_Started = FALSE; + +#ifdef BOT_LOGFILES + BOT_MsgOut( "STOPPED" ); +#endif // BOT_LOGFILES + } +} + + +// callback function to call the bot DoThink() function ----------------------- +// +PRIVATE +int BOT_CallThink( void* param ) +{ + ASSERT( param != NULL ); + BOT_ClientSide* pBot = (BOT_ClientSide*) param; + + pBot->DoThink(); + + return TRUE; +} + + +// console command for starting the client-side bot --------------------------- +// +PRIVATE +int Cmd_CLBOT_START( char* dummystr ) +{ + ASSERT( dummystr != NULL ); + HANDLE_COMMAND_DOMAIN_SEP( dummystr ); + + TheBot->SetShipObject( MyShip ); + TheBot->Start(); + + return TRUE; +} + +// console command for stopping the client-side bot --------------------------- +// +PRIVATE +int Cmd_CLBOT_STOP( char* dummystr ) +{ + ASSERT( dummystr != NULL ); + HANDLE_COMMAND_DOMAIN_SEP( dummystr ); + + TheBot->Stop(); + TheBot->SetShipObject( NULL ); + + return TRUE; +} + + +// console command for setting/getting the server we want to connect to ------- +// +PRIVATE +int Cmd_CLBOT_SERVER( char* pszServername ) +{ + // trim left + char *p = NULL; + for( p = pszServername; *p == ' '; p++ ); + + if ( *p == '\0' ) { + char* pszSetServer = TheBot->GetConnectServer(); + if ( pszSetServer == '\0' ) { + CON_AddLine( "no server set." ); + } else { + CON_AddLine( pszSetServer ); + } + } else { + TheBot->SetConnectServer( pszServername ); + } + + return TRUE; +} + +// console command to set/get the desired game status (connected/joined etc ) - +// +PRIVATE +int Cmd_CLBOT_GAMESTATUS( char* pszStatus ) +{ + ASSERT( pszStatus != NULL ); + HANDLE_COMMAND_DOMAIN_SEP( pszStatus ); + + int status = ( TheBot->GetDesiredConnStatus() << 1 ) | TheBot->GetDesiredJoinStatus(); + char *scan; + + if ( (scan = QueryIntArgumentEx( pszStatus, "%d", &status )) ) { + char *errpart; + long sval = strtol( scan, &errpart, 10 ); + if ( *errpart == 0 ) { + + TheBot->SetDesiredConnStatus( ( sval & 0x2 ) >> 1 ); + TheBot->SetDesiredJoinStatus( sval & 0x1 ); + } + } + + return TRUE; +} + + +// module registration function ----------------------------------------------- +// +REGISTER_MODULE( G_BOT_CL ) +{ + user_command_s regcom; + memset( ®com, 0, sizeof( user_command_s ) ); + + // register "clbot.start" command + regcom.command = "clbot.start"; + regcom.numparams = 0; + regcom.execute = Cmd_CLBOT_START; + regcom.statedump = NULL; + CON_RegisterUserCommand( ®com ); + + // register "clbot.start" command + regcom.command = "clbot.stop"; + regcom.numparams = 0; + regcom.execute = Cmd_CLBOT_STOP; + regcom.statedump = NULL; + CON_RegisterUserCommand( ®com ); + + // register "clbot.setsever" command + regcom.command = "clbot.server"; + regcom.numparams = 1; + regcom.execute = Cmd_CLBOT_SERVER; + regcom.statedump = NULL; + CON_RegisterUserCommand( ®com ); + + // register "clbot.gamestatus" command + regcom.command = "clbot.gamestatus"; + regcom.numparams = 1; + regcom.execute = Cmd_CLBOT_GAMESTATUS; + regcom.statedump = NULL; + CON_RegisterUserCommand( ®com ); +} + + |
