www.pudn.com > BucklandSimpleSoccer.rar > SoccerTeam.cpp
#include "SoccerTeam.h" #include "SoccerPitch.h" #include "Goal.h" #include "PlayerBase.h" #include "GoalKeeper.h" #include "FieldPlayer.h" #include "misc/utils.h" #include "SteeringBehaviors.h" #include "GoalKeeperStates.h" #include "ParamLoader.h" #include "2D/geometry.h" #include "Game/EntityManager.h" #include "Messaging/MessageDispatcher.h" #include "SoccerMessages.h" #include "TeamStates.h" #include "Debug/DebugConsole.h" #includeusing std::vector; //----------------------------- ctor ------------------------------------- // //------------------------------------------------------------------------ SoccerTeam::SoccerTeam(Goal* home_goal, Goal* opponents_goal, SoccerPitch* pitch, team_color color):m_pOpponentsGoal(opponents_goal), m_pHomeGoal(home_goal), m_pOpponents(NULL), m_pPitch(pitch), m_Color(color), m_dDistSqToBallOfClosestPlayer(0.0), m_pSupportingPlayer(NULL), m_pReceivingPlayer(NULL), m_pControllingPlayer(NULL), m_pPlayerClosestToBall(NULL) { //setup the state machine m_pStateMachine = new StateMachine (this); m_pStateMachine->SetCurrentState(Defending::Instance()); m_pStateMachine->SetPreviousState(Defending::Instance()); m_pStateMachine->SetGlobalState(NULL); //create the players and goalkeeper CreatePlayers(); //set default steering behaviors std::vector ::iterator it = m_Players.begin(); for (it; it != m_Players.end(); ++it) { (*it)->Steering()->SeparationOn(); } //create the sweet spot calculator m_pSupportSpotCalc = new SupportSpotCalculator(Prm.NumSupportSpotsX, Prm.NumSupportSpotsY, this); } //----------------------- dtor ------------------------------------------- // //------------------------------------------------------------------------ SoccerTeam::~SoccerTeam() { delete m_pStateMachine; std::vector ::iterator it = m_Players.begin(); for (it; it != m_Players.end(); ++it) { delete *it; } delete m_pSupportSpotCalc; } //-------------------------- update -------------------------------------- // // iterates through each player's update function and calculates // frequently accessed info //------------------------------------------------------------------------ void SoccerTeam::Update() { //this information is used frequently so it's more efficient to //calculate it just once each frame CalculateClosestPlayerToBall(); //the team state machine switches between attack/defense behavior. It //also handles the 'kick off' state where a team must return to their //kick off positions before the whistle is blown m_pStateMachine->Update(); //now update each player std::vector ::iterator it = m_Players.begin(); for (it; it != m_Players.end(); ++it) { (*it)->Update(); } } //------------------------ CalculateClosestPlayerToBall ------------------ // // sets m_iClosestPlayerToBall to the player closest to the ball //------------------------------------------------------------------------ void SoccerTeam::CalculateClosestPlayerToBall() { double ClosestSoFar = MaxFloat; std::vector ::iterator it = m_Players.begin(); for (it; it != m_Players.end(); ++it) { //calculate the dist. Use the squared value to avoid sqrt double dist = Vec2DDistanceSq((*it)->Pos(), Pitch()->Ball()->Pos()); //keep a record of this value for each player (*it)->SetDistSqToBall(dist); if (dist < ClosestSoFar) { ClosestSoFar = dist; m_pPlayerClosestToBall = *it; } } m_dDistSqToBallOfClosestPlayer = ClosestSoFar; } //------------- DetermineBestSupportingAttacker ------------------------ // // calculate the closest player to the SupportSpot //------------------------------------------------------------------------ PlayerBase* SoccerTeam::DetermineBestSupportingAttacker() { double ClosestSoFar = MaxFloat; PlayerBase* BestPlayer = NULL; std::vector ::iterator it = m_Players.begin(); for (it; it != m_Players.end(); ++it) { //only attackers utilize the BestSupportingSpot if ( ((*it)->Role() == PlayerBase::attacker) && ((*it) != m_pControllingPlayer) ) { //calculate the dist. Use the squared value to avoid sqrt double dist = Vec2DDistanceSq((*it)->Pos(), m_pSupportSpotCalc->GetBestSupportingSpot()); //if the distance is the closest so far and the player is not a //goalkeeper and the player is not the one currently controlling //the ball, keep a record of this player if ((dist < ClosestSoFar) ) { ClosestSoFar = dist; BestPlayer = (*it); } } } return BestPlayer; } //-------------------------- FindPass ------------------------------ // // The best pass is considered to be the pass that cannot be intercepted // by an opponent and that is as far forward of the receiver as possible //------------------------------------------------------------------------ bool SoccerTeam::FindPass(const PlayerBase*const passer, PlayerBase*& receiver, Vector2D& PassTarget, double power, double MinPassingDistance)const { std::vector ::const_iterator curPlyr = Members().begin(); double ClosestToGoalSoFar = MaxFloat; Vector2D Target; //iterate through all this player's team members and calculate which //one is in a position to be passed the ball for (curPlyr; curPlyr != Members().end(); ++curPlyr) { //make sure the potential receiver being examined is not this player //and that it is further away than the minimum pass distance if ( (*curPlyr != passer) && (Vec2DDistanceSq(passer->Pos(), (*curPlyr)->Pos()) > MinPassingDistance*MinPassingDistance)) { if (GetBestPassToReceiver(passer, *curPlyr, Target, power)) { //if the pass target is the closest to the opponent's goal line found // so far, keep a record of it double Dist2Goal = fabs(Target.x - OpponentsGoal()->Center().x); if (Dist2Goal < ClosestToGoalSoFar) { ClosestToGoalSoFar = Dist2Goal; //keep a record of this player receiver = *curPlyr; //and the target PassTarget = Target; } } } }//next team member if (receiver) return true; else return false; } //---------------------- GetBestPassToReceiver --------------------------- // // Three potential passes are calculated. One directly toward the receiver's // current position and two that are the tangents from the ball position // to the circle of radius 'range' from the receiver. // These passes are then tested to see if they can be intercepted by an // opponent and to make sure they terminate within the playing area. If // all the passes are invalidated the function returns false. Otherwise // the function returns the pass that takes the ball closest to the // opponent's goal area. //------------------------------------------------------------------------ bool SoccerTeam::GetBestPassToReceiver(const PlayerBase* const passer, const PlayerBase* const receiver, Vector2D& PassTarget, double power)const { //first, calculate how much time it will take for the ball to reach //this receiver, if the receiver was to remain motionless double time = Pitch()->Ball()->TimeToCoverDistance(Pitch()->Ball()->Pos(), receiver->Pos(), power); //return false if ball cannot reach the receiver after having been //kicked with the given power if (time < 0) return false; //the maximum distance the receiver can cover in this time double InterceptRange = time * receiver->MaxSpeed(); //Scale the intercept range const double ScalingFactor = 0.3; InterceptRange *= ScalingFactor; //now calculate the pass targets which are positioned at the intercepts //of the tangents from the ball to the receiver's range circle. Vector2D ip1, ip2; GetTangentPoints(receiver->Pos(), InterceptRange, Pitch()->Ball()->Pos(), ip1, ip2); const int NumPassesToTry = 3; Vector2D Passes[NumPassesToTry] = {ip1, receiver->Pos(), ip2}; // this pass is the best found so far if it is: // // 1. Further upfield than the closest valid pass for this receiver // found so far // 2. Within the playing area // 3. Cannot be intercepted by any opponents double ClosestSoFar = MaxFloat; bool bResult = false; for (int pass=0; pass Center().x); if (( dist < ClosestSoFar) && Pitch()->PlayingArea()->Inside(Passes[pass]) && isPassSafeFromAllOpponents(Pitch()->Ball()->Pos(), Passes[pass], receiver, power)) { ClosestSoFar = dist; PassTarget = Passes[pass]; bResult = true; } } return bResult; } //----------------------- isPassSafeFromOpponent ------------------------- // // test if a pass from 'from' to 'to' can be intercepted by an opposing // player //------------------------------------------------------------------------ bool SoccerTeam::isPassSafeFromOpponent(Vector2D from, Vector2D target, const PlayerBase* const receiver, const PlayerBase* const opp, double PassingForce)const { //move the opponent into local space. Vector2D ToTarget = target - from; Vector2D ToTargetNormalized = Vec2DNormalize(ToTarget); Vector2D LocalPosOpp = PointToLocalSpace(opp->Pos(), ToTargetNormalized, ToTargetNormalized.Perp(), from); //if opponent is behind the kicker then pass is considered okay(this is //based on the assumption that the ball is going to be kicked with a //velocity greater than the opponent's max velocity) if ( LocalPosOpp.x < 0 ) { return true; } //if the opponent is further away than the target we need to consider if //the opponent can reach the position before the receiver. if (Vec2DDistanceSq(from, target) < Vec2DDistanceSq(opp->Pos(), from)) { if (receiver) { if ( Vec2DDistanceSq(target, opp->Pos()) > Vec2DDistanceSq(target, receiver->Pos()) ) { return true; } else { return false; } } else { return true; } } //calculate how long it takes the ball to cover the distance to the //position orthogonal to the opponents position double TimeForBall = Pitch()->Ball()->TimeToCoverDistance(Vector2D(0,0), Vector2D(LocalPosOpp.x, 0), PassingForce); //now calculate how far the opponent can run in this time double reach = opp->MaxSpeed() * TimeForBall + Pitch()->Ball()->BRadius()+ opp->BRadius(); //if the distance to the opponent's y position is less than his running //range plus the radius of the ball and the opponents radius then the //ball can be intercepted if ( fabs(LocalPosOpp.y) < reach ) { return false; } return true; } //---------------------- isPassSafeFromAllOpponents ---------------------- // // tests a pass from position 'from' to position 'target' against each member // of the opposing team. Returns true if the pass can be made without // getting intercepted //------------------------------------------------------------------------ bool SoccerTeam::isPassSafeFromAllOpponents(Vector2D from, Vector2D target, const PlayerBase* const receiver, double PassingForce)const { std::vector ::const_iterator opp = Opponents()->Members().begin(); for (opp; opp != Opponents()->Members().end(); ++opp) { if (!isPassSafeFromOpponent(from, target, receiver, *opp, PassingForce)) { debug_on return false; } } return true; } //------------------------ CanShoot -------------------------------------- // // Given a ball position, a kicking power and a reference to a vector2D // this function will sample random positions along the opponent's goal- // mouth and check to see if a goal can be scored if the ball was to be // kicked in that direction with the given power. If a possible shot is // found, the function will immediately return true, with the target // position stored in the vector ShotTarget. //------------------------------------------------------------------------ bool SoccerTeam::CanShoot(Vector2D BallPos, double power, Vector2D& ShotTarget)const { //the number of randomly created shot targets this method will test int NumAttempts = Prm.NumAttemptsToFindValidStrike; while (NumAttempts--) { //choose a random position along the opponent's goal mouth. (making //sure the ball's radius is taken into account) ShotTarget = OpponentsGoal()->Center(); //the y value of the shot position should lay somewhere between two //goalposts (taking into consideration the ball diameter) int MinYVal = OpponentsGoal()->LeftPost().y + Pitch()->Ball()->BRadius(); int MaxYVal = OpponentsGoal()->RightPost().y - Pitch()->Ball()->BRadius(); ShotTarget.y = (double)RandInt(MinYVal, MaxYVal); //make sure striking the ball with the given power is enough to drive //the ball over the goal line. double time = Pitch()->Ball()->TimeToCoverDistance(BallPos, ShotTarget, power); //if it is, this shot is then tested to see if any of the opponents //can intercept it. if (time >= 0) { if (isPassSafeFromAllOpponents(BallPos, ShotTarget, NULL, power)) { return true; } } } return false; } //--------------------- ReturnAllFieldPlayersToHome --------------------------- // // sends a message to all players to return to their home areas forthwith //------------------------------------------------------------------------ void SoccerTeam::ReturnAllFieldPlayersToHome()const { std::vector ::const_iterator it = m_Players.begin(); for (it; it != m_Players.end(); ++it) { if ((*it)->Role() != PlayerBase::goal_keeper) { Dispatcher->DispatchMsg(SEND_MSG_IMMEDIATELY, 1, (*it)->ID(), Msg_GoHome, NULL); } } } //--------------------------- Render ------------------------------------- // // renders the players and any team related info //------------------------------------------------------------------------ void SoccerTeam::Render()const { std::vector ::const_iterator it = m_Players.begin(); for (it; it != m_Players.end(); ++it) { (*it)->Render(); } //show the controlling team and player at the top of the display if (Prm.bShowControllingTeam) { gdi->TextColor(Cgdi::white); if ( (Color() == blue) && InControl()) { gdi->TextAtPos(20,3,"Blue in Control"); } else if ( (Color() == red) && InControl()) { gdi->TextAtPos(20,3,"Red in Control"); } if (m_pControllingPlayer != NULL) { gdi->TextAtPos(Pitch()->cxClient()-150, 3, "Controlling Player: " + ttos(m_pControllingPlayer->ID())); } } //render the sweet spots if (Prm.bSupportSpots && InControl()) { m_pSupportSpotCalc->Render(); } //#define SHOW_TEAM_STATE #ifdef SHOW_TEAM_STATE if (Color() == red) { gdi->TextColor(Cgdi::white); if (CurrentState() == Attacking::Instance()) { gdi->TextAtPos(160, 20, "Attacking"); } if (CurrentState() == Defending::Instance()) { gdi->TextAtPos(160, 20, "Defending"); } if (CurrentState() == PrepareForKickOff::Instance()) { gdi->TextAtPos(160, 20, "Kickoff"); } } else { if (CurrentState() == Attacking::Instance()) { gdi->TextAtPos(160, Pitch()->cyClient()-40, "Attacking"); } if (CurrentState() == Defending::Instance()) { gdi->TextAtPos(160, Pitch()->cyClient()-40, "Defending"); } if (CurrentState() == PrepareForKickOff::Instance()) { gdi->TextAtPos(160, Pitch()->cyClient()-40, "Kickoff"); } } #endif //#define SHOW_SUPPORTING_PLAYERS_TARGET #ifdef SHOW_SUPPORTING_PLAYERS_TARGET if (m_pSupportingPlayer) { gdi->BlueBrush(); gdi->RedPen(); gdi->Circle(m_pSupportingPlayer->Steering()->Target(), 4); } #endif } //------------------------- CreatePlayers -------------------------------- // // creates the players //------------------------------------------------------------------------ void SoccerTeam::CreatePlayers() { if (Color() == blue) { //goalkeeper m_Players.push_back(new GoalKeeper(this, 1, TendGoal::Instance(), Vector2D(0,1), Vector2D(0.0, 0.0), Prm.PlayerMass, Prm.PlayerMaxForce, Prm.PlayerMaxSpeedWithoutBall, Prm.PlayerMaxTurnRate, Prm.PlayerScale)); //create the players m_Players.push_back(new FieldPlayer(this, 6, Wait::Instance(), Vector2D(0,1), Vector2D(0.0, 0.0), Prm.PlayerMass, Prm.PlayerMaxForce, Prm.PlayerMaxSpeedWithoutBall, Prm.PlayerMaxTurnRate, Prm.PlayerScale, PlayerBase::attacker)); m_Players.push_back(new FieldPlayer(this, 8, Wait::Instance(), Vector2D(0,1), Vector2D(0.0, 0.0), Prm.PlayerMass, Prm.PlayerMaxForce, Prm.PlayerMaxSpeedWithoutBall, Prm.PlayerMaxTurnRate, Prm.PlayerScale, PlayerBase::attacker)); m_Players.push_back(new FieldPlayer(this, 3, Wait::Instance(), Vector2D(0,1), Vector2D(0.0, 0.0), Prm.PlayerMass, Prm.PlayerMaxForce, Prm.PlayerMaxSpeedWithoutBall, Prm.PlayerMaxTurnRate, Prm.PlayerScale, PlayerBase::defender)); m_Players.push_back(new FieldPlayer(this, 5, Wait::Instance(), Vector2D(0,1), Vector2D(0.0, 0.0), Prm.PlayerMass, Prm.PlayerMaxForce, Prm.PlayerMaxSpeedWithoutBall, Prm.PlayerMaxTurnRate, Prm.PlayerScale, PlayerBase::defender)); } else { //goalkeeper m_Players.push_back(new GoalKeeper(this, 16, TendGoal::Instance(), Vector2D(0,-1), Vector2D(0.0, 0.0), Prm.PlayerMass, Prm.PlayerMaxForce, Prm.PlayerMaxSpeedWithoutBall, Prm.PlayerMaxTurnRate, Prm.PlayerScale)); //create the players m_Players.push_back(new FieldPlayer(this, 9, Wait::Instance(), Vector2D(0,-1), Vector2D(0.0, 0.0), Prm.PlayerMass, Prm.PlayerMaxForce, Prm.PlayerMaxSpeedWithoutBall, Prm.PlayerMaxTurnRate, Prm.PlayerScale, PlayerBase::attacker)); m_Players.push_back(new FieldPlayer(this, 11, Wait::Instance(), Vector2D(0,-1), Vector2D(0.0, 0.0), Prm.PlayerMass, Prm.PlayerMaxForce, Prm.PlayerMaxSpeedWithoutBall, Prm.PlayerMaxTurnRate, Prm.PlayerScale, PlayerBase::attacker)); m_Players.push_back(new FieldPlayer(this, 12, Wait::Instance(), Vector2D(0,-1), Vector2D(0.0, 0.0), Prm.PlayerMass, Prm.PlayerMaxForce, Prm.PlayerMaxSpeedWithoutBall, Prm.PlayerMaxTurnRate, Prm.PlayerScale, PlayerBase::defender)); m_Players.push_back(new FieldPlayer(this, 14, Wait::Instance(), Vector2D(0,-1), Vector2D(0.0, 0.0), Prm.PlayerMass, Prm.PlayerMaxForce, Prm.PlayerMaxSpeedWithoutBall, Prm.PlayerMaxTurnRate, Prm.PlayerScale, PlayerBase::defender)); } //register the players with the entity manager std::vector ::iterator it = m_Players.begin(); for (it; it != m_Players.end(); ++it) { EntityMgr->RegisterEntity(*it); } } PlayerBase* SoccerTeam::GetPlayerFromID(int id)const { std::vector ::const_iterator it = m_Players.begin(); for (it; it != m_Players.end(); ++it) { if ((*it)->ID() == id) return *it; } return NULL; } void SoccerTeam::SetPlayerHomeRegion(int plyr, int region)const { assert ( (plyr>=0) && (plyr SetHomeRegion(region); } //---------------------- UpdateTargetsOfWaitingPlayers ------------------------ // // void SoccerTeam::UpdateTargetsOfWaitingPlayers()const { std::vector ::const_iterator it = m_Players.begin(); for (it; it != m_Players.end(); ++it) { if ( (*it)->Role() != PlayerBase::goal_keeper ) { //cast to a field player FieldPlayer* plyr = static_cast (*it); if ( plyr->GetFSM()->isInState(*Wait::Instance()) || plyr->GetFSM()->isInState(*ReturnToHomeRegion::Instance()) ) { plyr->Steering()->SetTarget(plyr->HomeRegion()->Center()); } } } } //--------------------------- AllPlayersAtHome -------------------------------- // // returns false if any of the team are not located within their home region //----------------------------------------------------------------------------- bool SoccerTeam::AllPlayersAtHome()const { std::vector ::const_iterator it = m_Players.begin(); for (it; it != m_Players.end(); ++it) { if ((*it)->InHomeRegion() == false) { return false; } } return true; } //------------------------- RequestPass --------------------------------------- // // this tests to see if a pass is possible between the requester and // the controlling player. If it is possible a message is sent to the // controlling player to pass the ball asap. //----------------------------------------------------------------------------- void SoccerTeam::RequestPass(FieldPlayer* requester)const { //maybe put a restriction here if (RandFloat() > 0.1) return; if (isPassSafeFromAllOpponents(ControllingPlayer()->Pos(), requester->Pos(), requester, Prm.MaxPassingForce)) { //tell the player to make the pass //let the receiver know a pass is coming Dispatcher->DispatchMsg(SEND_MSG_IMMEDIATELY, requester->ID(), ControllingPlayer()->ID(), Msg_PassToMe, requester); } } //----------------------------- isOpponentWithinRadius ------------------------ // // returns true if an opposing player is within the radius of the position // given as a parameter //----------------------------------------------------------------------------- bool SoccerTeam::isOpponentWithinRadius(Vector2D pos, double rad) { std::vector ::const_iterator end = Opponents()->Members().end(); std::vector ::const_iterator it; for (it=Opponents()->Members().begin(); it !=end; ++it) { if (Vec2DDistanceSq(pos, (*it)->Pos()) < rad*rad) { return true; } } return false; }