/* ** Command & Conquer Generals Zero Hour(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: AIGuardRetaliate.h /*---------------------------------------------------------------------------*/ /* Electronic Arts Los Angeles */ /* Confidential Information */ /* Copyright (C) 2003 - All Rights Reserved */ /* DO NOT DISTRIBUTE */ /*---------------------------------------------------------------------------*/ /* Project: RTS3 */ /* File name: AIGuardRetaliate.h */ /* Created: Kris Morness July 2003 */ /* Desc: // Define Guard Retaliation states for AI */ /*---------------------------------------------------------------------------*/ #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine #include "Common/PerfTimer.h" #include "Common/Team.h" #include "Common/Xfer.h" #include "Common/ThingTemplate.h" #include "GameLogic/AI.h" #include "GameLogic/AIPathfind.h" #include "GameLogic/AIGuardRetaliate.h" #include "GameLogic/Module/AIUpdate.h" #include "GameLogic/Module/BodyModule.h" #include "GameLogic/Module/CollideModule.h" #include "GameLogic/Object.h" #include "GameLogic/PartitionManager.h" #include "GameLogic/PolygonTrigger.h" const Real CLOSE_ENOUGH = (25.0f); #ifdef _INTERNAL // for occasional debugging... //#pragma optimize("", off) //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes") #endif static Bool hasAttackedMeAndICanReturnFire( State *thisState, void* /*userData*/ ) { Object *obj = thisState->getMachineOwner(); BodyModuleInterface *bmi = obj ? obj->getBodyModule() : NULL; if (!(obj && bmi)) { return FALSE; } if (bmi->getClearableLastAttacker() == INVALID_ID) { return FALSE; } // K. It appears we have a valid aggressor. Find it, and determine if we can attack it, etc. Object *target = TheGameLogic->findObjectByID(bmi->getClearableLastAttacker()); bmi->clearLastAttacker(); // We use the clearable last attacker because we should continue attacking the guy. But if he // stops attacking us, then we want our timer to kick us off of him and make us go attack // other units instead. if (!target) { return FALSE; } if (obj->getRelationship(target) != ENEMIES) { return FALSE; } // This is a quick test on the target. It will be duplicated in getAbleToAttackSpecificObject, // but the payoff is worth the duplication. if (target->isEffectivelyDead()) { return FALSE; } //@todo: Get this out of here. Move it into the declaration of calling this function, or figure // out some way to call it less often. if (!obj->isAbleToAttack()) { return FALSE; } CanAttackResult result = obj->getAbleToAttackSpecificObject(ATTACK_NEW_TARGET, target, CMD_FROM_AI); if( result == ATTACKRESULT_POSSIBLE || result == ATTACKRESULT_POSSIBLE_AFTER_MOVING ) { return TRUE; } return FALSE; } //-- ExitConditions ------------------------------------------------------------------------------- /** * This returns true if the conditions specified have been met, false otherwise. */ Bool GuardRetaliateExitConditions::shouldExit(const StateMachine* machine) const { if (!machine->getGoalObject()) { if (m_conditionsToConsider & ATTACK_ExitIfNoUnitFound) { return true; } else { return false; } } if (m_conditionsToConsider & ATTACK_ExitIfExpiredDuration) { if (TheGameLogic->getFrame() >= m_attackGiveUpFrame) { return true; } } if (m_conditionsToConsider & ATTACK_ExitIfOutsideRadius) { Coord3D deltaAggressor, myRange; Coord3D objPos = *machine->getGoalObject()->getPosition(); Coord3D myPos = *machine->getOwner()->getPosition(); deltaAggressor.x = objPos.x - m_center.x; deltaAggressor.y = objPos.y - m_center.y; deltaAggressor.z = 0; // BGC - when we search for a target we don't account for Z, so why should we here? // changing this fixed a crash where a GLARebelInfantry would be in GuardReturnState, find // a target that is within range, then not be able to attack because its actually out of range. // then it would look for a new target, get the same one, and proceed in an infinite recursive // loop that eventually blew the stack. Real guardRangeSqr = sqr( AIGuardRetaliateMachine::getStdGuardRange( machine->getOwner() ) ); myRange.x = myPos.x - m_center.x; myRange.y = myPos.y - m_center.y; myRange.z = 0; if( deltaAggressor.lengthSqr() > m_radiusSqr ) { //The aggressor is too far away now... give up retaliation. return TRUE; } if( myRange.lengthSqr() > guardRangeSqr ) { //Never go beyond standard guard radius. return TRUE; } } return FALSE; } //-- AIGuardRetaliateMachine ------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------- AIGuardRetaliateMachine::AIGuardRetaliateMachine( Object *owner ) : StateMachine(owner, "AIGuardRetaliateMachine"), m_nemesisToAttack(INVALID_ID) { m_positionToGuard.zero(); static const StateConditionInfo attackAggressors[] = { StateConditionInfo(hasAttackedMeAndICanReturnFire, AI_GUARD_RETALIATE_ATTACK_AGGRESSOR, NULL), StateConditionInfo(NULL, NULL, NULL) // keep last }; // order matters: first state is the default state. // srj sez: I made "return" the start state, so that if ordered to guard a position // that isn't the unit's current position, it moves to that position first. defineState( AI_GUARD_RETALIATE_ATTACK_AGGRESSOR, newInstance(AIGuardRetaliateAttackAggressorState)( this ), AI_GUARD_RETALIATE_RETURN, AI_GUARD_RETALIATE_RETURN ); defineState( AI_GUARD_RETALIATE_RETURN, newInstance(AIGuardRetaliateReturnState)( this ), AI_GUARD_RETALIATE_IDLE, AI_GUARD_RETALIATE_INNER, attackAggressors ); defineState( AI_GUARD_RETALIATE_IDLE, newInstance(AIGuardRetaliateIdleState)( this ), AI_GUARD_RETALIATE_INNER, EXIT_MACHINE_WITH_SUCCESS, attackAggressors ); defineState( AI_GUARD_RETALIATE_INNER, newInstance(AIGuardRetaliateInnerState)( this ), AI_GUARD_RETALIATE_OUTER, AI_GUARD_RETALIATE_OUTER ); defineState( AI_GUARD_RETALIATE_OUTER, newInstance(AIGuardRetaliateOuterState)( this ), AI_GUARD_RETALIATE_GET_CRATE, AI_GUARD_RETALIATE_GET_CRATE ); defineState( AI_GUARD_RETALIATE_GET_CRATE, newInstance(AIGuardRetaliatePickUpCrateState)( this ), AI_GUARD_RETALIATE_RETURN, AI_GUARD_RETALIATE_RETURN ); } //-------------------------------------------------------------------------------------- AIGuardRetaliateMachine::~AIGuardRetaliateMachine() { } Bool AIGuardRetaliateMachine::isIdle() const { if( getCurrentStateID() == AI_IDLE ) { return TRUE; } return FALSE; } //-------------------------------------------------------------------------------------- /*static*/ Real AIGuardRetaliateMachine::getStdGuardRange(const Object* obj) { Real visionRange = TheAI->getAdjustedVisionRangeForObject(obj, AI_VISIONFACTOR_OWNERTYPE | AI_VISIONFACTOR_MOOD | AI_VISIONFACTOR_GUARDINNER); return visionRange; } //-------------------------------------------------------------------------------------- Bool AIGuardRetaliateMachine::lookForInnerTarget(void) { Object* owner = getOwner(); if (!owner->isAbleToAttack()) { return false; // my, that was easy } // Check if team auto targets same victim. Object *teamVictim = NULL; if (owner->getTeam()->getPrototype()->getTemplateInfo()->m_attackCommonTarget) { teamVictim = owner->getTeam()->getTeamTargetObject(); if (teamVictim) { setNemesisID(teamVictim->getID()); return true; // Transitions to AIGuardRetaliateInnerState. } } PartitionFilterRelationship f1(owner, PartitionFilterRelationship::ALLOW_ENEMIES); PartitionFilterPossibleToAttack f2(ATTACK_NEW_TARGET, owner, CMD_FROM_AI); PartitionFilterSameMapStatus filterMapStatus(owner); PartitionFilterRelationship f5(owner, PartitionFilterRelationship::ALLOW_NEUTRAL); PartitionFilterPossibleToEnter f6(owner, CMD_FROM_AI); PartitionFilterPossibleToHijack f7(owner, CMD_FROM_AI); PartitionFilterRejectBuildings f8( owner ); PartitionFilter *filters[16]; Int count = 0; // Enter Guard state if (owner->getTemplate()->isEnterGuard()) { filters[count++] = &f6; // Hijack Guard state if (owner->getTemplate()->isHijackGuard()) { filters[count++] = &f1; filters[count++] = &f7; } else { filters[count++] = &f5; } } // Attack Guard state else { filters[count++] = &f1; filters[count++] = &f2; filters[count++] = &f8; //Different than guard... we won't allow acquiring of structures (unless base defenses) } filters[count++] = &filterMapStatus; Real visionRange = AIGuardRetaliateMachine::getStdGuardRange(owner); filters[count++] = NULL; // SimpleObjectIterator* iter = ThePartitionManager->iterateObjectsInRange( // &pos, visionRange, FROM_CENTER_2D, filters, ITER_SORTED_NEAR_TO_FAR); // MemoryPoolObjectHolder hold(iter); // Object* target = iter->first(); // // srj sez: the above code is stupid and slow. since we only want the closest object, // just ask for that; the above has to find ALL objects in range, but we ignore all // but the first (closest). // const Coord3D *pos = getPositionToGuard(); Object* target = ThePartitionManager->getClosestObject(pos, visionRange, FROM_CENTER_2D, filters); if (target) { setNemesisID(target->getID()); return true; // Transitions to AIGuardRetaliateInnerState. } else { return false; } } // ------------------------------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------------------------------ void AIGuardRetaliateMachine::crc( Xfer *xfer ) { } // end crc // ------------------------------------------------------------------------------------------------ /** Xfer Method */ // ------------------------------------------------------------------------------------------------ void AIGuardRetaliateMachine::xfer( Xfer *xfer ) { // version XferVersion currentVersion = 2; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); if (version>=2) { StateMachine::xfer(xfer); // Forgot this in initial implementation. jba. } xfer->xferObjectID(&m_nemesisToAttack); xfer->xferCoord3D(&m_positionToGuard); } // end xfer // ------------------------------------------------------------------------------------------------ /** Load post process */ // ------------------------------------------------------------------------------------------------ void AIGuardRetaliateMachine::loadPostProcess( void ) { } // end loadPostProcess //-- AIGuardRetaliateInnerState ---------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------------------------------ void AIGuardRetaliateInnerState::crc( Xfer *xfer ) { } // end crc // ------------------------------------------------------------------------------------------------ /** Xfer Method */ // ------------------------------------------------------------------------------------------------ void AIGuardRetaliateInnerState::xfer( Xfer *xfer ) { // version XferVersion currentVersion = 1; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); } // end xfer // ------------------------------------------------------------------------------------------------ /** Load post process */ // ------------------------------------------------------------------------------------------------ void AIGuardRetaliateInnerState::loadPostProcess( void ) { onEnter(); } // end loadPostProcess //-------------------------------------------------------------------------------------- StateReturnType AIGuardRetaliateInnerState::onEnter( void ) { // See if we try to enter the target if (getMachineOwner()->getTemplate()->isEnterGuard()) { Object* nemesis = TheGameLogic->findObjectByID(getGuardMachine()->getNemesisID()) ; if (nemesis == NULL) { DEBUG_LOG(("Unexpected NULL nemesis in AIGuardRetaliateInnerState.\n")); return STATE_SUCCESS; } m_enterState = newInstance(AIEnterState)(getMachine()); m_enterState->getMachine()->setGoalObject(nemesis); StateReturnType returnVal = m_enterState->onEnter(); if (returnVal == STATE_CONTINUE) { return STATE_CONTINUE; } } // Or try to destroy the target else { Coord3D pos = *getGuardMachine()->getPositionToGuard(); Object* nemesis = TheGameLogic->findObjectByID(getGuardMachine()->getNemesisID()) ; if (nemesis == NULL) { DEBUG_LOG(("Unexpected NULL nemesis in AIGuardRetaliateInnerState.\n")); return STATE_SUCCESS; } m_exitConditions.m_center = pos; m_exitConditions.m_radiusSqr = sqr( 1.5f * AIGuardRetaliateMachine::getStdGuardRange( getMachineOwner() ) ); m_exitConditions.m_conditionsToConsider = (GuardRetaliateExitConditions::ATTACK_ExitIfOutsideRadius | GuardRetaliateExitConditions::ATTACK_ExitIfNoUnitFound); m_attackState = newInstance(AIAttackState)(getMachine(), false, true, false, &m_exitConditions); m_attackState->getMachine()->setGoalObject(nemesis); StateReturnType returnVal = m_attackState->onEnter(); if (returnVal == STATE_CONTINUE) { return STATE_CONTINUE; } } // if we had no one to attack, we were successful, so go to the next state. return STATE_SUCCESS; } //-------------------------------------------------------------------------------------- StateReturnType AIGuardRetaliateInnerState::update( void ) { if (m_attackState) { return m_attackState->update(); } else if (m_enterState) { return m_enterState->update(); } return STATE_SUCCESS; } //-------------------------------------------------------------------------------------- void AIGuardRetaliateInnerState::onExit( StateExitType status ) { Object *obj = getMachineOwner(); if (m_attackState) { m_attackState->onExit(status); m_attackState->deleteInstance(); m_attackState = NULL; } else if (m_enterState) { m_enterState->onExit(status); m_enterState->deleteInstance(); m_enterState = NULL; } if (obj->getTeam()) { obj->getTeam()->setTeamTargetObject(NULL); // clear the target. } } //-- AIGuardRetaliateOuterState ---------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------------------------------ void AIGuardRetaliateOuterState::crc( Xfer *xfer ) { } // end crc // ------------------------------------------------------------------------------------------------ /** Xfer Method */ // ------------------------------------------------------------------------------------------------ void AIGuardRetaliateOuterState::xfer( Xfer *xfer ) { // version XferVersion currentVersion = 1; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); } // end xfer // ------------------------------------------------------------------------------------------------ /** Load post process */ // ------------------------------------------------------------------------------------------------ void AIGuardRetaliateOuterState::loadPostProcess( void ) { AIGuardRetaliateOuterState onEnter(); } // end loadPostProcess //-------------------------------------------------------------------------------------- StateReturnType AIGuardRetaliateOuterState::onEnter( void ) { //if (getGuardMachine()->getGuardMode() == GUARDMODE_GUARD_WITHOUT_PURSUIT) //{ // // "patrol" mode does not follow targets outside the guard area. // return STATE_SUCCESS; //} Coord3D pos = *getGuardMachine()->getPositionToGuard(); Object* nemesis = TheGameLogic->findObjectByID(getGuardMachine()->getNemesisID()) ; if (nemesis == NULL) { DEBUG_LOG(("Unexpected NULL nemesis in AIGuardRetaliateOuterState.\n")); return STATE_SUCCESS; } Object *obj = getMachineOwner(); Real range = TheAI->getAdjustedVisionRangeForObject(obj, AI_VISIONFACTOR_OWNERTYPE | AI_VISIONFACTOR_MOOD); m_exitConditions.m_center = pos; m_exitConditions.m_radiusSqr = sqr( 0.67f * (range + AIGuardRetaliateMachine::getStdGuardRange( getMachineOwner() )) ); m_exitConditions.m_attackGiveUpFrame = TheGameLogic->getFrame() + TheAI->getAiData()->m_guardChaseUnitFrames; m_exitConditions.m_conditionsToConsider = (GuardRetaliateExitConditions::ATTACK_ExitIfExpiredDuration | GuardRetaliateExitConditions::ATTACK_ExitIfOutsideRadius | GuardRetaliateExitConditions::ATTACK_ExitIfNoUnitFound); m_attackState = newInstance(AIAttackState)(getMachine(), false, true, false, &m_exitConditions); m_attackState->getMachine()->setGoalObject(nemesis); StateReturnType returnVal = m_attackState->onEnter(); if (returnVal == STATE_CONTINUE) { return STATE_CONTINUE; } // if we had no one to attack, we were successful, so go to the next state. return STATE_SUCCESS; } //-------------------------------------------------------------------------------------- StateReturnType AIGuardRetaliateOuterState::update( void ) { if (m_attackState==NULL) return STATE_SUCCESS; Object* goalObj = m_attackState->getMachineGoalObject(); if (goalObj) { Coord3D deltaAggr; deltaAggr.x = m_exitConditions.m_center.x - goalObj->getPosition()->x; deltaAggr.y = m_exitConditions.m_center.y - goalObj->getPosition()->y; deltaAggr.z = m_exitConditions.m_center.z - goalObj->getPosition()->z; Real visionSqr = sqr(AIGuardRetaliateMachine::getStdGuardRange(getMachineOwner())); if (deltaAggr.lengthSqr() <= visionSqr) { // reset the counter m_exitConditions.m_attackGiveUpFrame = TheGameLogic->getFrame() + TheAI->getAiData()->m_guardChaseUnitFrames; } } return m_attackState->update(); } //-------------------------------------------------------------------------------------- void AIGuardRetaliateOuterState::onExit( StateExitType status ) { if (m_attackState) { m_attackState->onExit(status); m_attackState->deleteInstance(); m_attackState = NULL; } } //-- AIGuardRetaliateReturnState ---------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------------------------------ void AIGuardRetaliateReturnState::crc( Xfer *xfer ) { } // end crc // ------------------------------------------------------------------------------------------------ /** Xfer Method */ // ------------------------------------------------------------------------------------------------ void AIGuardRetaliateReturnState::xfer( Xfer *xfer ) { // version XferVersion currentVersion = 1; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); xfer->xferUnsignedInt(&m_nextReturnScanTime); } // end xfer // ------------------------------------------------------------------------------------------------ /** Load post process */ // ------------------------------------------------------------------------------------------------ void AIGuardRetaliateReturnState::loadPostProcess( void ) { } // end loadPostProcess //-------------------------------------------------------------------------------------- StateReturnType AIGuardRetaliateReturnState::onEnter( void ) { UnsignedInt now = TheGameLogic->getFrame(); m_nextReturnScanTime = now + GameLogicRandomValue(0, TheAI->getAiData()->m_guardEnemyReturnScanRate); // no, no, no, don't do this in onEnter, unless you like really slow maps. (srj) // if (getGuardMachine()->lookForInnerTarget()) // return STATE_FAILURE; // early termination because we found a target. m_goalPosition = *getGuardMachine()->getPositionToGuard(); AIUpdateInterface *ai = getMachineOwner()->getAIUpdateInterface(); if (ai && ai->isDoingGroundMovement()) { TheAI->pathfinder()->adjustDestination(getMachineOwner(), ai->getLocomotorSet(), &m_goalPosition); } setAdjustsDestination(true); return AIInternalMoveToState::onEnter(); } //-------------------------------------------------------------------------------------- StateReturnType AIGuardRetaliateReturnState::update( void ) { UnsignedInt now = TheGameLogic->getFrame(); if (now >= m_nextReturnScanTime) { m_nextReturnScanTime = now + TheAI->getAiData()->m_guardEnemyReturnScanRate; if (getGuardMachine()->lookForInnerTarget()) return STATE_FAILURE; // early termination because we found a target. } // Just let the return movement finish. return AIInternalMoveToState::update(); } //-------------------------------------------------------------------------------------- void AIGuardRetaliateReturnState::onExit( StateExitType status ) { AIInternalMoveToState::onExit( status ); } //-- AIGuardRetaliateIdleState ---------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------------------------------ void AIGuardRetaliateIdleState::crc( Xfer *xfer ) { } // end crc // ------------------------------------------------------------------------------------------------ /** Xfer Method */ // ------------------------------------------------------------------------------------------------ void AIGuardRetaliateIdleState::xfer( Xfer *xfer ) { // version XferVersion currentVersion = 1; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); xfer->xferUnsignedInt(&m_nextEnemyScanTime); } // end xfer // ------------------------------------------------------------------------------------------------ /** Load post process */ // ------------------------------------------------------------------------------------------------ void AIGuardRetaliateIdleState::loadPostProcess( void ) { } // end loadPostProcess //-------------------------------------------------------------------------------------- StateReturnType AIGuardRetaliateIdleState::onEnter( void ) { // first time thru, use a random amount so that everyone doesn't scan on the same frame, // to avoid "spikes". UnsignedInt now = TheGameLogic->getFrame(); m_nextEnemyScanTime = now + GameLogicRandomValue(0, TheAI->getAiData()->m_guardEnemyScanRate); return STATE_CONTINUE; } //-------------------------------------------------------------------------------------- StateReturnType AIGuardRetaliateIdleState::update( void ) { //DEBUG_LOG(("AIGuardRetaliateIdleState frame %d: %08lx\n",TheGameLogic->getFrame(),getMachineOwner())); UnsignedInt now = TheGameLogic->getFrame(); if (now < m_nextEnemyScanTime) return STATE_SLEEP(m_nextEnemyScanTime - now); m_nextEnemyScanTime = now + TheAI->getAiData()->m_guardEnemyScanRate; #ifdef STATE_MACHINE_DEBUG //getMachine()->setDebugOutput(true); #endif Object *owner = getMachineOwner(); AIUpdateInterface *ai = owner->getAIUpdateInterface(); // Check to see if we have created a crate we need to pick up. if (ai->getCrateID() != INVALID_ID) { getMachine()->setState(AI_GUARD_RETALIATE_GET_CRATE); return STATE_SLEEP(m_nextEnemyScanTime - now); } // if anyone is in the inner area, return success. if (getGuardMachine()->lookForInnerTarget()) { return STATE_SUCCESS; // Transitions to AIGuardRetaliateInnerState. } else { return STATE_FAILURE; } return STATE_SLEEP(m_nextEnemyScanTime - now); } //-------------------------------------------------------------------------------------- void AIGuardRetaliateIdleState::onExit( StateExitType status ) { } //-- AIGuardRetaliatePickUpCrateState ---------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- AIGuardRetaliatePickUpCrateState::AIGuardRetaliatePickUpCrateState( StateMachine *machine ) : AIPickUpCrateState(machine) { #ifdef STATE_MACHINE_DEBUG setName("AIGuardRetaliatePickUpCrateState"); #endif } //-------------------------------------------------------------------------------------- StateReturnType AIGuardRetaliatePickUpCrateState::onEnter( void ) { Object *owner = getMachineOwner(); AIUpdateInterface *ai = owner->getAIUpdateInterface(); // Check to see if we have created a crate we need to pick up. Object* crate = ai->checkForCrateToPickup(); if (crate) { getMachine()->setGoalObject(crate); return AIPickUpCrateState::onEnter(); } return STATE_SUCCESS; // no crate, so we're done. } //-------------------------------------------------------------------------------------- StateReturnType AIGuardRetaliatePickUpCrateState::update( void ) { return AIPickUpCrateState::update(); } //-------------------------------------------------------------------------------------- void AIGuardRetaliatePickUpCrateState::onExit( StateExitType status ) { } //-- AIGuardRetaliateAttackAggressorState ------------------------------------------------------------------ //------------------------------------------------------------------------------------------------- AIGuardRetaliateAttackAggressorState::AIGuardRetaliateAttackAggressorState( StateMachine *machine ) : State( machine, "AIGuardRetaliateAttackAggressorState" ) { m_attackState = NULL; } #ifdef STATE_MACHINE_DEBUG //---------------------------------------------------------------------------------------------------------- AsciiString AIGuardRetaliateAttackAggressorState::getName( ) const { AsciiString name = m_name; name.concat("/ "); if (m_attackState) name.concat(m_attackState->getName()); else name.concat("*NULL m_attackState"); return name; } #endif //------------------------------------------------------------------------------------------------- StateReturnType AIGuardRetaliateAttackAggressorState::onEnter( void ) { Object *obj = getMachineOwner(); ObjectID nemID = INVALID_ID; //Get the specified nemesis. If he doesn't exist... look at the object that damaged //us last. Object *nemesis = TheGameLogic->findObjectByID( getGuardMachine()->getNemesisID() ); BodyModuleInterface *body = obj->getBodyModule(); if( !nemesis && body && body->getLastDamageInfo()->in.m_sourceID && body->getLastDamageInfo()->in.m_damageType != DAMAGE_HEALING ) { nemID = obj->getBodyModule()->getLastDamageInfo()->in.m_sourceID; nemesis = TheGameLogic->findObjectByID( nemID ); if( nemesis && obj->getRelationship( nemesis ) == ENEMIES ) { getGuardMachine()->setNemesisID(nemID); } } if( !nemesis ) { DEBUG_LOG(("Unexpected NULL nemesis in AIGuardRetaliateAttackAggressorState.\n")); return STATE_SUCCESS; } Coord3D pos = *getGuardMachine()->getPositionToGuard(); Real range = TheAI->getAdjustedVisionRangeForObject(obj, AI_VISIONFACTOR_OWNERTYPE | AI_VISIONFACTOR_MOOD); m_exitConditions.m_center = pos; m_exitConditions.m_attackGiveUpFrame = TheGameLogic->getFrame() + TheAI->getAiData()->m_guardChaseUnitFrames; m_exitConditions.m_radiusSqr = sqr( range + AIGuardRetaliateMachine::getStdGuardRange( obj ) ); m_exitConditions.m_conditionsToConsider = ( GuardRetaliateExitConditions::ATTACK_ExitIfExpiredDuration | GuardRetaliateExitConditions::ATTACK_ExitIfOutsideRadius | GuardRetaliateExitConditions::ATTACK_ExitIfNoUnitFound ); m_attackState = newInstance(AIAttackState)(getMachine(), false, true, false, &m_exitConditions); m_attackState->getMachine()->setGoalObject(nemesis); StateReturnType returnVal = m_attackState->onEnter(); if (returnVal == STATE_CONTINUE) { return STATE_CONTINUE; } // if we had no one to attack, we were successful, so go to the next state. return STATE_SUCCESS; } //------------------------------------------------------------------------------------------------- StateReturnType AIGuardRetaliateAttackAggressorState::update( void ) { if (m_attackState==NULL) return STATE_SUCCESS; return m_attackState->update(); } //------------------------------------------------------------------------------------------------- void AIGuardRetaliateAttackAggressorState::onExit( StateExitType status ) { Object *obj = getMachineOwner(); if (m_attackState) { m_attackState->onExit(status); m_attackState->deleteInstance(); m_attackState = NULL; } if (obj->getTeam()) { obj->getTeam()->setTeamTargetObject(NULL); // clear the target. } } //------------------------------------------------------------------------------------------------- void AIGuardRetaliateAttackAggressorState::crc( Xfer *xfer ) { } //------------------------------------------------------------------------------------------------- void AIGuardRetaliateAttackAggressorState::xfer( Xfer *xfer ) { // version XferVersion currentVersion = 1; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); } //------------------------------------------------------------------------------------------------- void AIGuardRetaliateAttackAggressorState::loadPostProcess() { onEnter(); }