// check whether a ship is in jump range of a stargate ------------------------ // PRIVATE int ShipInStargateJumpRange( Stargate *stargate, ShipObject *ship, geomv_t range ) { ASSERT( stargate != NULL ); ASSERT( ship != NULL ); //NOTE: // the ship is treated as a point for jump // range detection. Vector3 gatenormal; FetchZVector( stargate->ObjPosition, &gatenormal ); Vertex3 gatepos; FetchTVector( stargate->ObjPosition, &gatepos ); Vertex3 shippos; FetchTVector( ship->ObjPosition, &shippos ); geomv_t shipdot = -DOT_PRODUCT( &gatenormal, &shippos ); geomv_t gatedot = -DOT_PRODUCT( &gatenormal, &gatepos ); geomv_t distance = shipdot - gatedot; // not in range if ship in wrong halfspace if ( GEOMV_NEGATIVE( distance ) ) { return FALSE; } // check whether inside of boundingsphere around stargate Vector3 stargate_ship; VECSUB( &stargate_ship, &shippos, &gatepos ); geomv_t stargate_ship_len = VctLenX( &stargate_ship ); if ( stargate_ship_len > stargate->BoundingSphere ) { return FALSE; } // inside the activation distance/range ? if ( distance < range ) { Vector3 shipnormal; FetchZVector( ship->ObjPosition, &shipnormal ); // ship must be flying approximately head-on into the gate //FIXME: cone_angle like for teleporter geomv_t dirdot = DOT_PRODUCT( &gatenormal, &shipnormal ); return ( dirdot > FLOAT_TO_GEOMV( 0.7f ) ); } return FALSE; }
// ---------------------------------------------------------------------------- // 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::_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 } }
// scan ship objects for radar ------------------------------------------------ // INLINE void RadarScanShipObjects() { GenObject *targetobj = NULL; for ( GenObject *walkobjs = FetchFirstShip(); walkobjs; ) { // set color for normal objects int rocolor = NORMAL_RADAR_OBJ_COL2; if ( AUX_ENABLE_DEPTHCUED_RADAR_DOTS && !AUX_ENABLE_ELITE_RADAR ) { Vertex3 shippos; FetchTVector( walkobjs->ObjPosition, &shippos ); Vertex3 myshippos; FetchTVector( MyShip->ObjPosition, &myshippos ); Vector3 dist; VECSUB( &dist, &shippos, &myshippos ); geomv_t vx = dist.X; geomv_t vy = dist.Y; geomv_t vz = dist.Z; ABS_GEOMV( vx ); ABS_GEOMV( vy ); ABS_GEOMV( vz ); int distance = 0; if ( ( vx > GEOMV_VANISHING ) || ( vy > GEOMV_VANISHING ) || ( vz > GEOMV_VANISHING ) ) { distance = GEOMV_TO_INT( VctLenX( &dist ) ); } if ( distance > 4096 ) { rocolor = NORMAL_RADAR_OBJ_COL3; } else if ( distance > 2048 ) { rocolor = NORMAL_RADAR_OBJ_COL2; } else { rocolor = NORMAL_RADAR_OBJ_COL1; } } // ensure that the target object is drawn last if ( walkobjs->HostObjNumber == TargetObjNumber ) { if ( targetobj == NULL ) { targetobj = walkobjs; if ( ( walkobjs = walkobjs->NextObj ) == NULL ) { walkobjs = targetobj; } continue; } rocolor = TARGET_RADAR_OBJ_COL; } // depict ship's position on radar if ( AUX_ENABLE_ELITE_RADAR ) { CalcDrawEliteRadarObj( walkobjs, rocolor ); } else { CalcDrawRadarObj( walkobjs, rocolor ); } if ( walkobjs == targetobj ) { break; } if ( ( walkobjs = walkobjs->NextObj ) == NULL ) { walkobjs = targetobj; } } }
// 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 }
// ---------------------------------------------------------------------------- // 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; } }