CSpaceObject *CZoanthropeAI::GetBase (void) const // GetBase // // Returns this ship's base { switch (GetCurrentOrder()) { case IShipController::orderGuard: case IShipController::orderPatrol: return GetCurrentOrderTarget(); default: return NULL; } }
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; } }
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; } } }
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; } } }
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; } } }
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; } }
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; } }
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; }
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; } }
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; } }
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; } }