Example #1
0
CSpaceObject *CZoanthropeAI::GetBase (void) const

//	GetBase
//
//	Returns this ship's base

	{
	switch (GetCurrentOrder())
		{
		case IShipController::orderGuard:
		case IShipController::orderPatrol:
			return GetCurrentOrderTarget();

		default:
			return NULL;
		}
	}
Example #2
0
void CFerianShipAI::OnObjDestroyedNotify (const SDestroyCtx &Ctx)

//	OnObjDestroyedNotify
//
//	Deal with an object that has been destroyed

	{
	switch (GetCurrentOrder())
		{
		case IShipController::orderMine:
			{
			if (Ctx.pObj == GetCurrentOrderTarget())
				{
				//	Avenge the base

				if (Ctx.pDestroyer && Ctx.pDestroyer->CanAttack() && !m_pShip->IsFriend(Ctx.pDestroyer))
					AddOrder(IShipController::orderDestroyTarget, Ctx.pDestroyer, 0);
				else if (m_State == stateAttackingThreat)
					AddOrder(IShipController::orderDestroyTarget, m_pTarget, 0);

				//	Stop mining

				CancelCurrentOrder();
				}
			break;
			}
		default:
			break;
		}

	//	Reset

	if (m_pBase == Ctx.pObj)
		{
		SetState(stateNone);
		m_pBase = NULL;
		}

	if (m_pTarget == Ctx.pObj)
		{
		SetState(stateNone);
		m_pTarget = NULL;
		}
	}
Example #3
0
void CZoanthropeAI::Behavior (void)

//	Behavior

	{
	//	Reset

	ResetBehavior();

	//	Behave according to our state

	switch (m_State)
		{
		case stateNone:
			BehaviorStart();
			break;

		case stateAttackingOnPatrol:
			{
			ASSERT(m_pTarget);
			ImplementCombatManeuvers(m_pTarget);
			m_AICtx.ImplementFireOnTargetsOfOpportunity(m_pShip, m_pTarget);

			//	See if we're out of our zone

			Metric rRange = LIGHT_SECOND * GetCurrentOrderData();
			if (CheckOutOfZone(GetCurrentOrderTarget(),
					rRange - PATROL_SENSOR_RANGE,
					rRange + PATROL_SENSOR_RANGE,
					30))
				SetState(stateNone);
			break;
			}

		case stateAttackingThreat:
			{
			ASSERT(m_pTarget);
			ImplementCombatManeuvers(m_pTarget);
			m_AICtx.ImplementFireOnTargetsOfOpportunity(m_pShip, m_pTarget);

			//	Every once in a while check to see if we've wandered too far from
			//	our base.

			if (CheckOutOfRange(GetCurrentOrderTarget(), PATROL_SENSOR_RANGE, 20))
				SetState(stateNone);
			break;
			}

		case stateOnCourseForStargate:
			{
			m_AICtx.ImplementGating(m_pShip, m_pBase);
			m_AICtx.ImplementAttackNearestTarget(m_pShip, ATTACK_RANGE, &m_pTarget);
			m_AICtx.ImplementFireOnTargetsOfOpportunity(m_pShip);
			break;
			}

		case stateOnEscortCourse:
			{
			ASSERT(m_pBase);

			CVector vFlockPos;
			CVector vFlockVel;
			int iFlockFacing;
			if (m_AICtx.CalcFlockingFormation(m_pShip, m_pBase, MAX_FLOCK_DIST, SEPARATION_RANGE, &vFlockPos, &vFlockVel, &iFlockFacing))
				{
				m_AICtx.ImplementFormationManeuver(m_pShip, vFlockPos, vFlockVel, m_pShip->AlignToRotationAngle(iFlockFacing));
				}
			else
				{
				CVector vTarget = m_pBase->GetPos() - m_pShip->GetPos();
				Metric rTargetDist2 = vTarget.Dot(vTarget);

				m_AICtx.ImplementCloseOnTarget(m_pShip, m_pBase, vTarget, rTargetDist2);
				m_AICtx.ImplementAttackNearestTarget(m_pShip, m_AICtx.GetMaxWeaponRange(), &m_pTarget);
				m_AICtx.ImplementFireOnTargetsOfOpportunity(m_pShip);
				}

			//	See if there is anything to attack

			CSpaceObject *pTarget;
			if (CheckForEnemiesInRange(m_pShip, PATROL_SENSOR_RANGE, 30, &pTarget))
				SetState(stateAttackingThreat, m_pBase, pTarget);

			break;
			}

		case stateOnPatrolOrbit:
			{
			ASSERT(m_pBase);

			CVector vFlockPos;
			CVector vFlockVel;
			int iFlockFacing;
			if (m_AICtx.CalcFlockingFormation(m_pShip, NULL, MAX_FLOCK_DIST, SEPARATION_RANGE, &vFlockPos, &vFlockVel, &iFlockFacing))
				{
				m_AICtx.ImplementFormationManeuver(m_pShip, vFlockPos, vFlockVel, m_pShip->AlignToRotationAngle(iFlockFacing));
				}
			else
				{
				m_AICtx.ImplementOrbit(m_pShip, m_pBase, LIGHT_SECOND * GetCurrentOrderData());
				m_AICtx.ImplementAttackNearestTarget(m_pShip, m_AICtx.GetMaxWeaponRange(), &m_pTarget);
				m_AICtx.ImplementFireOnTargetsOfOpportunity(m_pShip);
				}

			//	See if there is anything to attack

			CSpaceObject *pTarget;
			if (CheckForEnemiesInRange(m_pShip, PATROL_SENSOR_RANGE, 30, &pTarget))
				SetState(stateAttackingOnPatrol, m_pBase, pTarget);
			break;
			}

		case stateReturningFromThreat:
			{
			ASSERT(m_pBase);
			m_AICtx.ImplementDocking(m_pShip, m_pBase);
			m_AICtx.ImplementAttackNearestTarget(m_pShip, m_AICtx.GetMaxWeaponRange(), &m_pTarget);
			m_AICtx.ImplementFireOnTargetsOfOpportunity(m_pShip);

			//	See if there is anything to attack

			CSpaceObject *pTarget;
			if (CheckForEnemiesInRange(m_pBase, PATROL_SENSOR_RANGE, 30, &pTarget))
				SetState(stateAttackingThreat, m_pBase, pTarget);
			break;
			}

		case stateWaiting:
			m_AICtx.ImplementStop(m_pShip);

			if (m_pTarget)
				m_AICtx.ImplementAttackTarget(m_pShip, m_pTarget, true);

			m_AICtx.ImplementAttackNearestTarget(m_pShip, m_AICtx.GetMaxWeaponRange(), &m_pTarget);
			m_AICtx.ImplementFireOnTargetsOfOpportunity(m_pShip);
			break;

		case stateWaitingForThreat:
			{
			ASSERT(m_pBase);
			CSpaceObject *pTarget;
			if (CheckForEnemiesInRange(m_pBase, PATROL_SENSOR_RANGE, 30, &pTarget))
				SetState(stateAttackingThreat, m_pBase, pTarget);
			break;
			}
		}
	}
Example #4
0
void CZoanthropeAI::BehaviorStart (void)

//	BehaviorStart
//
//	Figure out what to do based on orders

	{
	switch (GetCurrentOrder())
		{
		case IShipController::orderNone:
			{
			if (m_pShip->GetDockedObj() == NULL)
				AddOrder(IShipController::orderGate, NULL, IShipController::SData());
			break;
			}

		case IShipController::orderEscort:
			{
			CSpaceObject *pPrincipal = GetCurrentOrderTarget();
			ASSERT(pPrincipal);

			SetState(stateOnEscortCourse, pPrincipal);
			m_pShip->Communicate(m_pBase, msgEscortReportingIn, m_pShip);
			break;
			}

		case IShipController::orderFollowPlayerThroughGate:
			SetState(stateOnCourseForStargate, m_pShip->GetNearestStargate());
			break;

		case IShipController::orderGate:
			{
			//	Look for the gate

			CSpaceObject *pGate = GetCurrentOrderTarget();
			if (pGate == NULL)
				pGate = m_pShip->GetNearestStargate(true);

			//	Head for the gate

			if (pGate)
				SetState(stateOnCourseForStargate, pGate);

			break;
			}

		case IShipController::orderGuard:
			{
			CSpaceObject *pPrincipal = GetCurrentOrderTarget();
			ASSERT(pPrincipal);

			//	If we're not docked, dock with principal

			if (m_pShip->GetDockedObj() == NULL)
				SetState(stateReturningFromThreat, pPrincipal);

			//	Otherwise, wait for a threat

			else
				SetState(stateWaitingForThreat, pPrincipal);

			break;
			}

		case IShipController::orderPatrol:
			{
			CSpaceObject *pPrincipal = GetCurrentOrderTarget();
			ASSERT(pPrincipal);

			SetState(stateOnPatrolOrbit, pPrincipal);
			break;
			}

		case IShipController::orderWaitForPlayer:
			{
			SetState(stateWaiting);
			break;
			}
		}
	}
Example #5
0
void CAutonAI::BehaviorStart (void)

//	BehaviorStart
//
//	Initiate behavior state based on orders

	{
	switch (GetCurrentOrder())
		{
		case IShipController::orderNone:
			{
			if (m_pShip->GetDockedObj() == NULL)
				AddOrder(IShipController::orderGate, NULL, IShipController::SData());
			break;
			}

		case IShipController::orderEscort:
			{
			//	If this is a support ship, then we follow. Otherwise we
			//	are an armed escort.

			if (m_AICtx.IsNonCombatant())
				SetState(stateFollowing);
			else
				SetState(stateEscorting);

			m_pDest = GetCurrentOrderTarget();
			ASSERT(m_pDest);

			m_pShip->Communicate(m_pDest, msgEscortReportingIn, m_pShip);
			break;
			}

		case IShipController::orderFollowPlayerThroughGate:
			{
			SetState(stateOnCourseForStargate);
			m_pDest = m_pShip->GetNearestStargate();
			break;
			}

		case IShipController::orderGate:
			{
			//	Look for the gate

			CSpaceObject *pGate = GetCurrentOrderTarget();
			if (pGate == NULL)
				pGate = m_pShip->GetNearestStargate(true);

			//	Head for the gate

			if (pGate)
				{
				SetState(stateOnCourseForStargate);
				m_pDest = pGate;
				}

			break;
			}

		case IShipController::orderWaitForPlayer:
			{
			SetState(stateWaiting);
			break;
			}
		}
	}
Example #6
0
DWORD CAutonAI::OnCommunicate (CSpaceObject *pSender, MessageTypes iMessage, CSpaceObject *pParam1, DWORD dwParam2)

//	Communicate
//
//	Handle communications from other objects

	{
	switch (iMessage)
		{
		case msgAttack:
		case msgAttackDeter:
			{
			if (GetCurrentOrder() == IShipController::orderEscort
					&& !m_AICtx.IsNonCombatant())
				{
				SetState(stateAttackingTarget);
				m_pTarget = pParam1;
				return resAck;
				}
			else
				return resNoAnswer;
			}

		case msgAbort:
			{
			SetState(stateNone);
			return resAck;
			}

		case msgFormUp:
			{
			if (m_State == stateWaiting || m_State == stateAttackingTarget)
				{
				SetState(stateNone);
				return resAck;
				}
			else
				return resNoAnswer;
			}

		case msgQueryCommunications:
			{
			if (GetCurrentOrder() == IShipController::orderEscort)
				{
				DWORD dwRes = 0;
				if (!m_AICtx.IsNonCombatant())
					dwRes |= resCanAttack;
				if (m_State == stateAttackingTarget)
					dwRes |= (resCanAbortAttack | resCanFormUp);
				if (m_State != stateWaiting)
					dwRes |= resCanWait;
				else
					dwRes |= resCanFormUp;

				return dwRes;
				}
			else
				return 0;
			}

		case msgQueryEscortStatus:
			{
			if (GetEscortPrincipal() == pParam1)
				return resAck;
			else
				return resNoAnswer;
			}

		case msgQueryWaitStatus:
			return (m_State == stateWaiting ? resAck : resNoAnswer);

		case msgWait:
			{
			if (GetCurrentOrder() == IShipController::orderEscort)
				{
				SetState(stateWaiting);
				m_pDest = GetCurrentOrderTarget();
				return resAck;
				}
			else
				return resNoAnswer;
			}

		default:
			return resNoAnswer;
		}
	}
Example #7
0
void CFerianShipAI::BehaviorStart (void)

//	BehaviorStart
//
//	Figure out what to do based on orders

	{
	switch (GetCurrentOrder())
		{
		case IShipController::orderNone:
			{
			if (m_pShip->GetDockedObj() == NULL)
				AddOrder(IShipController::orderGate, NULL, 0);
			break;
			}

		case IShipController::orderDestroyTarget:
			{
			SetState(stateAttackingTarget);
			m_pTarget = GetCurrentOrderTarget();
			ASSERT(m_pTarget);
			ASSERT(m_pTarget->DebugIsValid() && m_pTarget->NotifyOthersWhenDestroyed());
			break;
			}

		case IShipController::orderMine:
			{
			m_pBase = GetCurrentOrderTarget();
			ASSERT(m_pBase);

			SetState(stateOnCourseForMine);
			m_pTarget = FindRandomAsteroid();
			if (m_pTarget == NULL)
				{
				SetState(stateOnCourseForStargate);
				m_pBase = m_pShip->GetNearestStargate(true);
				}
			break;
			}

		case IShipController::orderGate:
			{
			//	Look for the gate

			CSpaceObject *pGate = GetCurrentOrderTarget();
			if (pGate == NULL)
				pGate = m_pShip->GetNearestStargate(true);

			//	Head for the gate

			if (pGate)
				{
				SetState(stateOnCourseForStargate);
				m_pBase = pGate;
				}

			break;
			}

		case IShipController::orderDestroyPlayerOnReturn:
			{
			CSpaceObject *pGate = m_pShip->GetNearestStargate();
			if (pGate)
				{
				SetState(stateWaitForPlayerAtGate);
				m_pBase = pGate;
				}
			break;
			}
		default:
			break;
		}
	}
Example #8
0
void CFleetShipAI::OnObjDestroyedNotify (const SDestroyCtx &Ctx)

//	OnObjDestroyedNotify
//
//	Deal with an object that has been destroyed

	{
	switch (GetCurrentOrder())
		{
		case IShipController::orderEscort:
			if (Ctx.pObj == GetCurrentOrderTarget())
				{
				CancelCurrentOrder();

				//	Get the orders of the leader

				IShipController::OrderTypes iLeaderOrders = IShipController::orderNone;
				CSpaceObject *pLeaderTarget = NULL;
				if (Ctx.pObj && Ctx.pObj->GetCategory() == CSpaceObject::catShip)
					{
					CShip *pLeader = Ctx.pObj->AsShip();
					if (pLeader)
						iLeaderOrders = pLeader->GetController()->GetCurrentOrderEx(&pLeaderTarget);
					}

				//	Avenge the leader

				int iAvengeChance = (pLeaderTarget ? 40 : 100);
				if (Ctx.pDestroyer 
						&& Ctx.pDestroyer != pLeaderTarget
						&& Ctx.pDestroyer->CanAttack() 
						&& !m_pShip->IsFriend(Ctx.pDestroyer)
						&& mathRandom(1, 100) <= iAvengeChance)
					AddOrder(IShipController::orderDestroyTarget, Ctx.pDestroyer, 0);

				//	Take on leader's orders

				switch (iLeaderOrders)
					{
					case IShipController::orderDestroyTarget:
					case IShipController::orderGuard:
						if (pLeaderTarget)
							AddOrder(iLeaderOrders, pLeaderTarget, 0);
						break;
					default:
						break;
					}

				//	Attack other enemies

				AddOrder(IShipController::orderAttackNearestEnemy, NULL, 0);
				}
			break;

		case IShipController::orderDock:
		case IShipController::orderDestroyTarget:
		case IShipController::orderPatrol:
		case IShipController::orderGuard:
			if (Ctx.pObj == GetCurrentOrderTarget())
				CancelCurrentOrder();
			break;
		default:
			break;
		}

	//	If our target gets destroyed...

	switch (m_State)
		{
		case stateAttackTarget:
		case stateAttackOnPatrol:
			if (Ctx.pObj == m_pTarget)
				SetState(stateNone);
			break;
		default:
			break;
		}

	//	Reset

	if (m_pDest == Ctx.pObj)
		m_pDest = NULL;
	if (m_pTarget == Ctx.pObj)
		m_pTarget = NULL;
	}
Example #9
0
DWORD CFleetShipAI::OnCommunicate (CSpaceObject *pSender, MessageTypes iMessage, CSpaceObject *pParam1, DWORD dwParam2)

//	Communicate
//
//	Handle communications from other objects

	{
	switch (iMessage)
		{
		case msgAbort:
			{
			if (m_State == stateAttackTarget
					|| m_State == stateAttackAtWill
					|| m_State == stateAttackInFormation)
				SetState(stateNone);
			return resAck;
			}

		case msgAttack:
			{
			if (GetCurrentOrder() == IShipController::orderEscort)
				{
				SetState(stateAttackTarget);
				m_pTarget = pParam1;
				m_iCounter = 0;
				ASSERT(m_pTarget);
				return resAck;
				}
			else
				return resNoAnswer;
			}

		case msgAttackInFormation:
			{
			if (GetCurrentOrder() == IShipController::orderEscort)
				{
				SetState(stateAttackInFormation);
				m_iCounter = dwParam2;
				return resAck;
				}
			else
				return resNoAnswer;
			}

		case msgBreakAndAttack:
			{
			if (GetCurrentOrder() == IShipController::orderEscort)
				{
				SetState(stateAttackAtWill);
				return resAck;
				}
			else
				return resNoAnswer;
			}

		case msgFormUp:
			{
			if (GetCurrentOrder() == IShipController::orderEscort)
				{
				if (dwParam2 != 0xffffffff)
					SetCurrentOrderData(dwParam2);

				SetState(stateNone);
				return resAck;
				}
			else
				return resNoAnswer;
			}

		case msgQueryCommunications:
			{
			if (GetCurrentOrder() == IShipController::orderEscort && GetCurrentOrderTarget() == pSender)
				{
				DWORD dwRes = (resCanBeInFormation | resCanAttack | resCanBreakAndAttack);
				if (m_State == stateAttackTarget
						|| m_State == stateAttackAtWill
						|| m_State == stateAttackInFormation)
					dwRes |= resCanAbortAttack;
				else if (m_State == stateKeepFormation)
					dwRes |= resCanAttackInFormation;

				return dwRes;
				}
			else
				return resNoAnswer;
			}

		case msgQueryEscortStatus:
		case msgQueryFleetStatus:
			{
			if (GetEscortPrincipal() == pParam1)
				return resAck;
			else
				return resNoAnswer;
			}

		default:
			return resNoAnswer;
		}
	}
Example #10
0
void CFleetShipAI::BehaviorStart (void)

//	BehaviorStart
//
//	Figure out what to do based on orders

	{
	switch (GetCurrentOrder())
		{
		case IShipController::orderNone:
			{
			if (m_pShip->GetDockedObj() == NULL)
				AddOrder(IShipController::orderGate, NULL, 0);
			break;
			}

		case IShipController::orderAttackNearestEnemy:
			{
			CSpaceObject *pTarget = m_pShip->GetNearestEnemy(ATTACK_AT_WILL_RANGE, true);
			if (pTarget)
				{
				SetState(stateAttackTarget);
				m_pTarget = pTarget;
				m_iCounter = 0;
				}
			else
				CancelCurrentOrder();
			break;
			}

		case IShipController::orderDestroyTarget:
			{
			SetState(stateAttackTarget);
			m_pTarget = GetCurrentOrderTarget();
			m_iCounter = 0;
			ASSERT(m_pTarget);
			break;
			}

		case IShipController::orderDock:
			{
			CSpaceObject *pDest = GetCurrentOrderTarget();
			ASSERT(pDest);

			//	If we're docked with our destination then we're done.

			if (m_pShip->GetDockedObj() == pDest)
				CancelCurrentOrder();

			//	Otherwise, try to dock

			else
				{
				SetState(stateOnCourseForDocking);
				m_pDest = pDest;
				}

			break;
			}

		case IShipController::orderEscort:
			{
			SetState(stateKeepFormation);

			m_pLeader = GetCurrentOrderTarget();
			ASSERT(m_pLeader);

			m_iFormation = (int)HIWORD(GetCurrentOrderData());
			m_iPlace = (int)LOWORD(GetCurrentOrderData());
			ASSERT(m_iFormation < FORMATIONS_COUNT);

			//	If there is no place for this ship in the formation, then
			//	gate-out

			if (m_iFormation >= FORMATIONS_COUNT
					|| m_iPlace >= g_Formations[m_iFormation].iCount)
				{
				CancelCurrentOrder();
				break;
				}

			//	Keep formation

			m_pShip->Communicate(m_pLeader, msgEscortReportingIn, m_pShip);
			break;
			}

		case IShipController::orderFollowPlayerThroughGate:
			{
			SetState(stateOnCourseForStargate);
			m_pDest = m_pShip->GetNearestStargate();
			break;
			}

		case IShipController::orderGate:
			{
			//	Look for the gate

			CSpaceObject *pGate = GetCurrentOrderTarget();
			if (pGate == NULL)
				pGate = m_pShip->GetNearestStargate(true);

			//	Head for the gate

			if (pGate)
				{
				SetState(stateOnCourseForStargate);
				m_pDest = pGate;
				}

			break;
			}

		case IShipController::orderPatrol:
		case IShipController::orderGuard:
			{
			SetState(stateOnPatrolOrbit);
			m_pDest = GetCurrentOrderTarget();
			ASSERT(m_pDest);
			break;
			}
		default:
			break;
		}
	}
Example #11
0
void CFleetShipAI::Behavior (void)

//	Behavior

	{
	//	Reset

	ResetBehavior();

	//	Use basic items

	UseItemsBehavior();

	//	Behave according to our state

	switch (m_State)
		{
		case stateNone:
			BehaviorStart();
			break;

		case stateAttackAtWill:
			{
			//	If we don't have a target, find one

			if (m_pTarget == NULL && m_pShip->IsDestinyTime(13))
				m_pTarget = m_pShip->GetNearestEnemy(ATTACK_AT_WILL_RANGE, true);

			//	If we have a target, attack

			if (m_pTarget)
				{
				ImplementAttackTarget(m_pTarget);
				ImplementFireOnTargetsOfOpportunity(m_pTarget);
				}

			//	Otherwise, stay in formation

			else
				{
				ImplementKeepFormation();
				ImplementFireOnTargetsOfOpportunity();
				ImplementFireOnNearestTarget();
				}

			break;
			}

		case stateAttackInFormation:
			{
			bool bInFormation;
			ImplementKeepFormation(&bInFormation);

			//	Fire the primary weapon (most of the time)

			if (bInFormation && mathRandom(1, 3) > 1)
				ImplementFireWeapon();

			//	Decrement counter

			if (m_iCounter > 0)
				{
				if (--m_iCounter == 0)
					{
					SetState(stateKeepFormation);
					m_iCounter = 0;
					}
				}

			break;
			}

		case stateAttackOnPatrol:
			ASSERT(m_pTarget);
			ImplementAttackTarget(m_pTarget);
			ImplementFireOnTargetsOfOpportunity(m_pTarget);

			//	Check to see if we've wandered outside our patrol zone

			if (m_pShip->IsDestinyTime(20))
				{
				CSpaceObject *pCenter = GetCurrentOrderTarget();
				Metric rMaxRange2 = PATROL_ORBIT_DIST + PATROL_SENSOR_RANGE;
				rMaxRange2 = rMaxRange2 * rMaxRange2;
				Metric rMinRange2 = std::max(0.0, PATROL_ORBIT_DIST - PATROL_SENSOR_RANGE);
				rMinRange2 = rMinRange2 * rMinRange2;

				int iTick = m_pShip->GetSystem()->GetTick();
				CVector vRange = pCenter->GetPos() - m_pShip->GetPos();
				Metric rDistance2 = vRange.Dot(vRange);

				//	If we're outside of our patrol range then stop the attack

				if (rDistance2 > rMaxRange2 || rDistance2 < rMinRange2)
					SetState(stateNone);
				}
			break;

		case stateAttackTarget:
			{
			ASSERT(m_pTarget);
			if (m_pTarget->CanMove())
				ImplementAttackTarget(m_pTarget);
			else
				{
				//	Attack the target as best we can

				CVector vTarget = m_pTarget->GetPos() - m_pShip->GetPos();
				Metric rTargetDist2 = vTarget.Dot(vTarget);
				ImplementFireWeaponOnTarget(-1, -1, m_pTarget, vTarget, rTargetDist2);

				//	Maneuver to a proper position near the target

				Metric rRange;
				int iCounterAdj;
				if (m_pAISettings->iCombatStyle == aicombatStandOff)
					{
					rRange = m_rBestWeaponRange / 2.0;
					iCounterAdj = 2;
					}
				else
					{
					rRange = m_rBestWeaponRange / 6.0;
					iCounterAdj = 1;
					}

				if (m_iCounter == 0)
					{
					m_vVector = ComputeAttackPos(m_pTarget, rRange, &m_iAngle);
					m_iCounter = mathRandom(130, 160) * iCounterAdj;
					}
				else
					m_iCounter--;

				ImplementFormationManeuver(m_vVector, NullVector, m_iAngle);
				}

			ImplementFireOnTargetsOfOpportunity(m_pTarget);
			break;
			}

		case stateKeepFormation:
			ImplementKeepFormation();
			ImplementFireOnTargetsOfOpportunity();
			ImplementFireOnNearestTarget();
			break;

		case stateOnCourseForDocking:
			ASSERT(m_pDest);
			ImplementDocking(m_pDest);
			ImplementFireOnTargetsOfOpportunity();
			ImplementFireOnNearestTarget();
			break;

		case stateOnCourseForStargate:
			ASSERT(m_pDest);
			ImplementGating(m_pDest);
			ImplementFireOnTargetsOfOpportunity();
			ImplementFireOnNearestTarget();
			break;

		case stateOnPatrolOrbit:
			ASSERT(m_pDest);
			ImplementOrbit(m_pDest, PATROL_ORBIT_DIST);
			ImplementAttackNearestTarget(m_rMaxWeaponRange, &m_pTarget, m_pDest);
			ImplementFireOnTargetsOfOpportunity(NULL, m_pDest);

			//	Check to see if any enemy ships appear

			if (m_pShip->IsDestinyTime(30))
				{
				CSpaceObject *pPrincipal = GetCurrentOrderTarget();
				CSpaceObject *pTarget = CalcEnemyShipInRange(pPrincipal, PATROL_SENSOR_RANGE, m_pDest);
				if (pTarget)
					{
					SetState(stateAttackOnPatrol);
					m_pTarget = pTarget;
					ASSERT(m_pTarget->DebugIsValid() && m_pTarget->NotifyOthersWhenDestroyed());
					}
				}
			break;
		}
	}