www.pudn.com > BucklandSimpleSoccer.rar > FieldPlayerStates.cpp


#include "FieldPlayerStates.h" 
#include "Debug/DebugConsole.h" 
#include "SoccerPitch.h" 
#include "FieldPlayer.h" 
#include "SteeringBehaviors.h" 
#include "SoccerTeam.h" 
#include "Goal.h" 
#include "2D/geometry.h" 
#include "SoccerBall.h" 
#include "ParamLoader.h" 
#include "Messaging/Telegram.h" 
#include "Messaging/MessageDispatcher.h" 
#include "SoccerMessages.h" 
 
#include "time/Regulator.h" 
 
 
//uncomment below to send state info to the debug window 
#define PLAYER_STATE_INFO_ON 
 
 
//************************************************************************ Global state 
 
GlobalPlayerState* GlobalPlayerState::Instance() 
{ 
  static GlobalPlayerState instance; 
 
  return &instance; 
} 
 
 
void GlobalPlayerState::Execute(FieldPlayer* player)                                      
{ 
  //if a player is in possession and close to the ball reduce his max speed 
  if((player->BallWithinReceivingRange()) && (player->isControllingPlayer())) 
  { 
    player->SetMaxSpeed(Prm.PlayerMaxSpeedWithBall); 
  } 
 
  else 
  { 
     player->SetMaxSpeed(Prm.PlayerMaxSpeedWithoutBall); 
  } 
     
} 
 
 
bool GlobalPlayerState::OnMessage(FieldPlayer* player, const Telegram& telegram) 
{ 
  switch(telegram.Msg) 
  { 
  case Msg_ReceiveBall: 
    { 
      //set the target 
      player->Steering()->SetTarget(*(static_cast(telegram.ExtraInfo))); 
 
      //change state  
      player->GetFSM()->ChangeState(ReceiveBall::Instance()); 
 
      return true; 
    } 
 
    break; 
 
  case Msg_SupportAttacker: 
    { 
      //if already supporting just return 
      if (player->GetFSM()->isInState(*SupportAttacker::Instance())) 
      { 
        return true; 
      } 
       
      //set the target to be the best supporting position 
      player->Steering()->SetTarget(player->Team()->GetSupportSpot()); 
 
      //change the state 
      player->GetFSM()->ChangeState(SupportAttacker::Instance()); 
 
      return true; 
    } 
 
    break; 
 
 case Msg_Wait: 
    { 
      //change the state 
      player->GetFSM()->ChangeState(Wait::Instance()); 
 
      return true; 
    } 
 
    break; 
 
  case Msg_GoHome: 
    { 
      player->SetDefaultHomeRegion(); 
       
      player->GetFSM()->ChangeState(ReturnToHomeRegion::Instance()); 
 
      return true; 
    } 
 
    break; 
 
  case Msg_PassToMe: 
    {   
       
      //get the position of the player requesting the pass  
      FieldPlayer* receiver = static_cast(telegram.ExtraInfo); 
 
      #ifdef PLAYER_STATE_INFO_ON 
      debug_con << "Player " << player->ID() << " received request from " << 
                    receiver->ID() << " to make pass" << ""; 
      #endif 
 
      //if the ball is not within kicking range or their is already a  
      //receiving player, this player cannot pass the ball to the player 
      //making the request. 
      if (player->Team()->Receiver() != NULL || 
         !player->BallWithinKickingRange() ) 
      { 
        #ifdef PLAYER_STATE_INFO_ON 
        debug_con << "Player " << player->ID() << " cannot make requested pass " << ""; 
        #endif 
 
        return true; 
      } 
       
      //make the pass    
      player->Ball()->Kick(receiver->Pos() - player->Ball()->Pos(), 
                           Prm.MaxPassingForce); 
 
           
     #ifdef PLAYER_STATE_INFO_ON 
     debug_con << "Player " << player->ID() << " Passed ball to requesting player" << ""; 
     #endif 
         
      //let the receiver know a pass is coming  
      Dispatcher->DispatchMsg(SEND_MSG_IMMEDIATELY, 
                              player->ID(), 
                              receiver->ID(), 
                              Msg_ReceiveBall, 
                              &receiver->Pos()); 
 
    
 
      //change state    
      player->GetFSM()->ChangeState(Wait::Instance()); 
 
      player->FindSupport(); 
 
      return true; 
    } 
 
    break; 
 
  }//end switch 
 
  return false; 
} 
                                 
 
        
 
//***************************************************************************** CHASEBALL 
 
ChaseBall* ChaseBall::Instance() 
{ 
  static ChaseBall instance; 
 
  return &instance; 
} 
 
 
void ChaseBall::Enter(FieldPlayer* player) 
{ 
  player->Steering()->SeekOn(); 
 
  #ifdef PLAYER_STATE_INFO_ON 
  debug_con << "Player " << player->ID() << " enters chase state" << ""; 
  #endif 
} 
 
void ChaseBall::Execute(FieldPlayer* player)                                      
{ 
  //if the ball is within kicking range the player changes state to KickBall. 
  if (player->BallWithinKickingRange()) 
  { 
    player->GetFSM()->ChangeState(KickBall::Instance()); 
     
    return; 
  } 
                                                                               
  //if the player is the closest player to the ball then he should keep 
  //chasing it 
  if (player->isClosestTeamMemberToBall()) 
  { 
    player->Steering()->SetTarget(player->Ball()->Pos()); 
 
    return; 
  } 
   
  //if the player is not closest to the ball anymore, he should return back 
  //to his home region and wait for another opportunity 
  player->GetFSM()->ChangeState(ReturnToHomeRegion::Instance()); 
} 
 
 
void ChaseBall::Exit(FieldPlayer* player) 
{ 
  player->Steering()->SeekOff(); 
} 
 
 
 
//*****************************************************************************SUPPORT ATTACKING PLAYER 
 
SupportAttacker* SupportAttacker::Instance() 
{ 
  static SupportAttacker instance; 
 
  return &instance; 
} 
 
 
void SupportAttacker::Enter(FieldPlayer* player) 
{ 
  player->Steering()->ArriveOn(); 
 
  player->Steering()->SetTarget(player->Team()->GetSupportSpot()); 
   
  #ifdef PLAYER_STATE_INFO_ON 
  debug_con << "Player " << player->ID() << " enters support state" << ""; 
  #endif 
} 
 
void SupportAttacker::Execute(FieldPlayer* player)                                      
{ 
  //if his team loses control go back home 
  if (!player->Team()->InControl()) 
  { 
    player->GetFSM()->ChangeState(ReturnToHomeRegion::Instance()); return; 
  }  
 
 
  //if the best supporting spot changes, change the steering target 
  if (player->Team()->GetSupportSpot() != player->Steering()->Target()) 
  {     
    player->Steering()->SetTarget(player->Team()->GetSupportSpot()); 
 
    player->Steering()->ArriveOn(); 
  } 
 
  //if this player has a shot at the goal AND the attacker can pass 
  //the ball to him the attacker should pass the ball to this player 
  if( player->Team()->CanShoot(player->Pos(), 
                               Prm.MaxShootingForce)) 
  { 
    player->Team()->RequestPass(player); 
  } 
 
 
  //if this player is located at the support spot and his team still have 
  //possession, he should remain still and turn to face the ball 
  if (player->AtTarget()) 
  { 
    player->Steering()->ArriveOff(); 
         
    //the player should keep his eyes on the ball! 
    player->TrackBall(); 
 
    player->SetVelocity(Vector2D(0,0)); 
 
    //if not threatened by another player request a pass 
    if (!player->isThreatened()) 
    { 
      player->Team()->RequestPass(player); 
    } 
  } 
} 
 
 
void SupportAttacker::Exit(FieldPlayer* player) 
{ 
  //set supporting player to null so that the team knows it has to  
  //determine a new one. 
  player->Team()->SetSupportingPlayer(NULL); 
 
  player->Steering()->ArriveOff(); 
} 
 
 
 
 
//************************************************************************ RETURN TO HOME REGION 
 
ReturnToHomeRegion* ReturnToHomeRegion::Instance() 
{ 
  static ReturnToHomeRegion instance; 
 
  return &instance; 
} 
 
 
void ReturnToHomeRegion::Enter(FieldPlayer* player) 
{ 
  player->Steering()->ArriveOn(); 
 
  if (!player->HomeRegion()->Inside(player->Steering()->Target(), Region::halfsize)) 
  { 
    player->Steering()->SetTarget(player->HomeRegion()->Center()); 
  } 
 
  #ifdef PLAYER_STATE_INFO_ON 
  debug_con << "Player " << player->ID() << " enters ReturnToHome state" << ""; 
  #endif 
} 
 
void ReturnToHomeRegion::Execute(FieldPlayer* player) 
{ 
  if (player->Pitch()->GameOn()) 
  { 
    //if the ball is nearer this player than any other team member  && 
    //there is not an assigned receiver && the goalkeeper does not gave 
    //the ball, go chase it 
    if ( player->isClosestTeamMemberToBall() && 
         (player->Team()->Receiver() == NULL) && 
         !player->Pitch()->GoalKeeperHasBall()) 
    { 
      player->GetFSM()->ChangeState(ChaseBall::Instance()); 
 
      return; 
    } 
  } 
 
  //if game is on and close enough to home, change state to wait and set the  
  //player target to his current position.(so that if he gets jostled out of  
  //position he can move back to it) 
  if (player->Pitch()->GameOn() && player->HomeRegion()->Inside(player->Pos(), 
                                                             Region::halfsize)) 
  { 
    player->Steering()->SetTarget(player->Pos()); 
    player->GetFSM()->ChangeState(Wait::Instance()); 
  } 
  //if game is not on the player must return much closer to the center of his 
  //home region 
  else if(!player->Pitch()->GameOn() && player->AtTarget()) 
  { 
    player->GetFSM()->ChangeState(Wait::Instance()); 
  } 
} 
 
void ReturnToHomeRegion::Exit(FieldPlayer* player) 
{ 
  player->Steering()->ArriveOff(); 
} 
 
 
 
 
//***************************************************************************** WAIT 
 
Wait* Wait::Instance() 
{ 
  static Wait instance; 
 
  return &instance; 
} 
 
 
void Wait::Enter(FieldPlayer* player) 
{ 
  #ifdef PLAYER_STATE_INFO_ON 
  debug_con << "Player " << player->ID() << " enters wait state" << ""; 
  #endif 
 
  //if the game is not on make sure the target is the center of the player's 
  //home region. This is ensure all the players are in the correct positions 
  //ready for kick off 
  if (!player->Pitch()->GameOn()) 
  { 
    player->Steering()->SetTarget(player->HomeRegion()->Center()); 
  } 
} 
 
void Wait::Execute(FieldPlayer* player) 
{     
  //if the player has been jostled out of position, get back in position   
  if (!player->AtTarget()) 
  { 
    player->Steering()->ArriveOn(); 
 
    return; 
  } 
 
  else 
  { 
    player->Steering()->ArriveOff(); 
 
    player->SetVelocity(Vector2D(0,0)); 
 
    //the player should keep his eyes on the ball! 
    player->TrackBall(); 
  } 
 
  //if this player's team is controlling AND this player is not the attacker 
  //AND is further up the field than the attacker he should request a pass. 
  if ( player->Team()->InControl()    && 
     (!player->isControllingPlayer()) && 
       player->isAheadOfAttacker() ) 
  { 
    player->Team()->RequestPass(player); 
 
    return; 
  } 
 
  if (player->Pitch()->GameOn()) 
  { 
   //if the ball is nearer this player than any other team member  AND 
    //there is not an assigned receiver AND neither goalkeeper has 
    //the ball, go chase it 
   if (player->isClosestTeamMemberToBall() && 
       player->Team()->Receiver() == NULL  && 
       !player->Pitch()->GoalKeeperHasBall()) 
   { 
     player->GetFSM()->ChangeState(ChaseBall::Instance()); 
 
     return; 
   } 
  }  
} 
 
void Wait::Exit(FieldPlayer* player){} 
 
 
 
 
//************************************************************************ KICK BALL 
 
KickBall* KickBall::Instance() 
{ 
  static KickBall instance; 
 
  return &instance; 
} 
 
 
void KickBall::Enter(FieldPlayer* player) 
{ 
  //let the team know this player is controlling 
   player->Team()->SetControllingPlayer(player); 
    
   //the player can only make so many kick attempts per second. 
   if (!player->isReadyForNextKick())  
   { 
     player->GetFSM()->ChangeState(ChaseBall::Instance()); 
   } 
 
    
  #ifdef PLAYER_STATE_INFO_ON 
  debug_con << "Player " << player->ID() << " enters kick state" << ""; 
  #endif 
} 
 
void KickBall::Execute(FieldPlayer* player) 
{  
  //calculate the dot product of the vector pointing to the ball 
  //and the player's heading 
  Vector2D ToBall = player->Ball()->Pos() - player->Pos(); 
  double   dot    = player->Heading().Dot(Vec2DNormalize(ToBall));  
 
  //cannot kick the ball if the goalkeeper is in possession or if it is  
  //behind the player or if there is already an assigned receiver. So just 
  //continue chasing the ball 
  if (player->Team()->Receiver() != NULL   || 
      player->Pitch()->GoalKeeperHasBall() || 
      (dot < 0) )  
  { 
    #ifdef PLAYER_STATE_INFO_ON 
    debug_con << "Goaly has ball / ball behind player" << ""; 
    #endif 
     
    player->GetFSM()->ChangeState(ChaseBall::Instance()); 
 
    return; 
  } 
 
  /* Attempt a shot at the goal */ 
 
  //if a shot is possible, this vector will hold the position along the  
  //opponent's goal line the player should aim for. 
  Vector2D    BallTarget; 
 
  //the dot product is used to adjust the shooting force. The more 
  //directly the ball is ahead, the more forceful the kick 
  double power = Prm.MaxShootingForce * dot; 
 
  //if it is determined that the player could score a goal from this position 
  //OR if he should just kick the ball anyway, the player will attempt 
  //to make the shot 
  if (player->Team()->CanShoot(player->Ball()->Pos(), 
                               power, 
                               BallTarget)                   ||  
     (RandFloat() < Prm.ChancePlayerAttemptsPotShot)) 
  { 
   #ifdef PLAYER_STATE_INFO_ON 
   debug_con << "Player " << player->ID() << " attempts a shot at " << BallTarget << ""; 
   #endif 
 
   //add some noise to the kick. We don't want players who are  
   //too accurate! The amount of noise can be adjusted by altering 
   //Prm.PlayerKickingAccuracy 
   BallTarget = AddNoiseToKick(player->Ball()->Pos(), BallTarget); 
 
   //this is the direction the ball will be kicked in 
   Vector2D KickDirection = BallTarget - player->Ball()->Pos(); 
    
   player->Ball()->Kick(KickDirection, power); 
     
   //change state    
   player->GetFSM()->ChangeState(Wait::Instance()); 
    
   player->FindSupport(); 
   
   return; 
 } 
 
 
  /* Attempt a pass to a player */ 
 
  //if a receiver is found this will point to it 
  PlayerBase* receiver = NULL; 
 
  power = Prm.MaxPassingForce * dot; 
   
  //test if there are any potential candidates available to receive a pass 
  if (player->isThreatened()  && 
      player->Team()->FindPass(player, 
                              receiver, 
                              BallTarget, 
                              power, 
                              Prm.MinPassDist)) 
  {      
    //add some noise to the kick 
    BallTarget = AddNoiseToKick(player->Ball()->Pos(), BallTarget); 
 
    Vector2D KickDirection = BallTarget - player->Ball()->Pos(); 
    
    player->Ball()->Kick(KickDirection, power); 
 
    #ifdef PLAYER_STATE_INFO_ON 
    debug_con << "Player " << player->ID() << " passes the ball with force " << power << "  to player "  
              << receiver->ID() << "  Target is " << BallTarget << ""; 
    #endif 
 
     
    //let the receiver know a pass is coming  
    Dispatcher->DispatchMsg(SEND_MSG_IMMEDIATELY, 
                            player->ID(), 
                            receiver->ID(), 
                            Msg_ReceiveBall, 
                            &BallTarget);                             
    
 
    //the player should wait at his current position unless instruced 
    //otherwise   
    player->GetFSM()->ChangeState(Wait::Instance()); 
 
    player->FindSupport(); 
 
    return; 
  } 
 
  //cannot shoot or pass, so dribble the ball upfield 
  else 
  {    
    player->FindSupport(); 
 
    player->GetFSM()->ChangeState(Dribble::Instance()); 
  }    
} 
 
 
//*************************************************************************** DRIBBLE 
 
Dribble* Dribble::Instance() 
{ 
  static Dribble instance; 
 
  return &instance; 
} 
 
 
void Dribble::Enter(FieldPlayer* player) 
{ 
  //let the team know this player is controlling 
  player->Team()->SetControllingPlayer(player); 
 
#ifdef PLAYER_STATE_INFO_ON 
  debug_con << "Player " << player->ID() << " enters dribble state" << ""; 
  #endif 
} 
 
void Dribble::Execute(FieldPlayer* player) 
{ 
  double dot = player->Team()->HomeGoal()->Facing().Dot(player->Heading()); 
 
  //if the ball is between the player and the home goal, it needs to swivel 
  // the ball around by doing multiple small kicks and turns until the player  
  //is facing in the correct direction 
  if (dot < 0) 
  { 
    //the player's heading is going to be rotated by a small amount (Pi/4)  
    //and then the ball will be kicked in that direction 
    Vector2D direction = player->Heading(); 
 
    //calculate the sign (+/-) of the angle between the player heading and the  
    //facing direction of the goal so that the player rotates around in the  
    //correct direction 
    double angle = QuarterPi * -1 * 
                 player->Team()->HomeGoal()->Facing().Sign(player->Heading()); 
 
    Vec2DRotateAroundOrigin(direction, angle); 
 
    //this value works well whjen the player is attempting to control the 
    //ball and turn at the same time 
    const double KickingForce = 0.8; 
 
    player->Ball()->Kick(direction, KickingForce); 
  } 
 
  //kick the ball down the field 
  else 
  { 
    player->Ball()->Kick(player->Team()->HomeGoal()->Facing(), 
                         Prm.MaxDribbleForce);   
  } 
 
  //the player has kicked the ball so he must now change state to follow it 
  player->GetFSM()->ChangeState(ChaseBall::Instance()); 
     
  return;   
} 
 
 
 
//************************************************************************     RECEIVEBALL 
 
ReceiveBall* ReceiveBall::Instance() 
{ 
  static ReceiveBall instance; 
 
  return &instance; 
} 
 
 
void ReceiveBall::Enter(FieldPlayer* player) 
{ 
  //let the team know this player is receiving the ball 
  player->Team()->SetReceiver(player); 
   
  //this player is also now the controlling player 
  player->Team()->SetControllingPlayer(player); 
 
  //there are two types of receive behavior. One uses arrive to direct 
  //the receiver to the position sent by the passer in its telegram. The 
  //other uses the pursuit behavior to pursue the ball.  
  //This statement selects between them dependent on the probability 
  //ChanceOfUsingArriveTypeReceiveBehavior, whether or not an opposing 
  //player is close to the receiving player, and whether or not the receiving 
  //player is in the opponents 'hot region' (the third of the pitch closest 
  //to the opponent's goal 
  const double PassThreatRadius = 70.0; 
 
  if (( player->InHotRegion() || 
        RandFloat() < Prm.ChanceOfUsingArriveTypeReceiveBehavior) && 
     !player->Team()->isOpponentWithinRadius(player->Pos(), PassThreatRadius)) 
  { 
    player->Steering()->ArriveOn(); 
     
    #ifdef PLAYER_STATE_INFO_ON 
    debug_con << "Player " << player->ID() << " enters receive state (Using Arrive)" << ""; 
    #endif 
  } 
  else 
  { 
    player->Steering()->PursuitOn(); 
 
    #ifdef PLAYER_STATE_INFO_ON 
    debug_con << "Player " << player->ID() << " enters receive state (Using Pursuit)" << ""; 
    #endif 
  } 
} 
 
void ReceiveBall::Execute(FieldPlayer* player) 
{ 
  //if the ball comes close enough to the player or if his team lose control 
  //he should change state to chase the ball 
  if (player->BallWithinReceivingRange() || !player->Team()->InControl()) 
  { 
    player->GetFSM()->ChangeState(ChaseBall::Instance()); 
 
    return; 
  }   
 
  if (player->Steering()->PursuitIsOn()) 
  { 
    player->Steering()->SetTarget(player->Ball()->Pos()); 
  } 
 
  //if the player has 'arrived' at the steering target he should wait and 
  //turn to face the ball 
  if (player->AtTarget()) 
  { 
    player->Steering()->ArriveOff(); 
    player->Steering()->PursuitOff(); 
    player->TrackBall();     
    player->SetVelocity(Vector2D(0,0)); 
  }  
} 
 
void ReceiveBall::Exit(FieldPlayer* player) 
{ 
  player->Steering()->ArriveOff(); 
  player->Steering()->PursuitOff(); 
 
  player->Team()->SetReceiver(NULL); 
}