/* ** Command & Conquer Generals(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. // // // //////////////////////////////////////////////////////////////////////////////// // TurretAI.h // Author: Steven Johnson, April 2002 // Simple turret behavior #pragma once #ifndef _TURRETAI_H_ #define _TURRETAI_H_ #include "Common/StateMachine.h" #include "Common/GameMemory.h" const Real DEFAULT_TURN_RATE = 0.01f; const Real DEFAULT_PITCH_RATE = 0.01f; /** * The TurretAI state IDs. * Each of these constants will be associated with an instance of a State class * in a given StateMachine. */ enum TurretStateType { TURRETAI_IDLE, TURRETAI_IDLESCAN, TURRETAI_AIM, ///< aim turret at GoalObject TURRETAI_FIRE, ///< fire turret at GoalObject TURRETAI_RECENTER, ///< rotate turret back to default position TURRETAI_HOLD, ///< hold turret position for a bit before recenter NUM_TURRETAI_STATES }; // FORWARD DECLARATIONS /////////////////////////////////////////////////////////////////////////// class Weapon; class TurretAI; //----------------------------------------------------------------------------------------------------------- /** * The AI state machine. This is used by AIUpdate to implement all of the * commands in the AICommandInterface. */ class TurretStateMachine : public StateMachine { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( TurretStateMachine, "TurretStateMachine" ); public: /** * The implementation of this constructor defines the states * used by this machine. */ TurretStateMachine( TurretAI* tai, Object* owner, AsciiString name ); TurretAI* getTurretAI() const { return m_turretAI; } virtual void clear(); virtual StateReturnType resetToDefaultState(); virtual StateReturnType setState( StateID newStateID ); private: TurretAI* m_turretAI; protected: // snapshot interface virtual void crc( Xfer *xfer ); virtual void xfer( Xfer *xfer ); virtual void loadPostProcess(); }; //----------------------------------------------------------------------------------------------------------- class TurretState : public State { MEMORY_POOL_GLUE_ABC(TurretState) protected: TurretState( TurretStateMachine* machine, AsciiString name ) : State( machine, name) { } TurretAI* getTurretAI() { return ((TurretStateMachine*)getMachine())->getTurretAI(); } }; EMPTY_DTOR(TurretState) //----------------------------------------------------------------------------------------------------------- class TurretAIIdleState : public TurretState { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(TurretAIIdleState, "TurretAIIdleState") public: TurretAIIdleState( TurretStateMachine* machine ) : TurretState( machine, "TurretAIIdleState"), m_nextIdleScan(0) { } virtual StateReturnType onEnter(); virtual StateReturnType update(); protected: // snapshot interface virtual void crc( Xfer *xfer ); virtual void xfer( Xfer *xfer ); virtual void loadPostProcess(); private: void resetIdleScan(); UnsignedInt m_nextIdleScan; }; EMPTY_DTOR(TurretAIIdleState) //----------------------------------------------------------------------------------------------------------- class TurretAIIdleScanState : public TurretState { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(TurretAIIdleScanState, "TurretAIIdleScanState") public: TurretAIIdleScanState( TurretStateMachine* machine ) : TurretState( machine, "TurretAIIdleScanState"), m_desiredAngle(0) { } virtual StateReturnType onEnter(); virtual void onExit( StateExitType status ); virtual StateReturnType update(); protected: // snapshot interface virtual void crc( Xfer *xfer ); virtual void xfer( Xfer *xfer ); virtual void loadPostProcess(); private: Real m_desiredAngle; }; EMPTY_DTOR(TurretAIIdleScanState) //----------------------------------------------------------------------------------------------------------- /** * Aim a turret at GoalObject */ class TurretAIAimTurretState : public TurretState { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(TurretAIAimTurretState, "TurretAIAimTurretState") private: #ifdef INTER_TURRET_DELAY UnsignedInt m_extraDelay; #endif public: TurretAIAimTurretState( TurretStateMachine* machine ) : TurretState( machine, "TurretAIAimTurretState" ) { } virtual StateReturnType onEnter(); virtual void onExit( StateExitType status ); virtual StateReturnType update(); protected: // snapshot interface STUBBED - no member vars to save. jba. virtual void crc( Xfer *xfer ){}; virtual void xfer( Xfer *xfer ){XferVersion cv = 1; XferVersion v = cv; xfer->xferVersion( &v, cv );} virtual void loadPostProcess(){}; }; EMPTY_DTOR(TurretAIAimTurretState) //----------------------------------------------------------------------------------------------------------- /** * Rotate a turret back to its default position */ class TurretAIRecenterTurretState : public TurretState { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(TurretAIRecenterTurretState, "TurretAIRecenterTurretState") public: TurretAIRecenterTurretState( TurretStateMachine* machine ) : TurretState( machine, "TurretAIRecenterTurretState" ) { } virtual StateReturnType onEnter(); virtual void onExit( StateExitType status ); virtual StateReturnType update(); protected: // snapshot interface STUBBED - no member vars to save. jba. virtual void crc( Xfer *xfer ){}; virtual void xfer( Xfer *xfer ){XferVersion cv = 1; XferVersion v = cv; xfer->xferVersion( &v, cv );} virtual void loadPostProcess(){}; }; EMPTY_DTOR(TurretAIRecenterTurretState) //----------------------------------------------------------------------------------------------------------- /** * Hold turret position for a bit before recenter */ class TurretAIHoldTurretState : public TurretState { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(TurretAIHoldTurretState, "TurretAIHoldTurretState") private: UnsignedInt m_timestamp; ///< frame this state was last entered public: TurretAIHoldTurretState( TurretStateMachine* machine ) : TurretState( machine , "AIHoldTurretState") { m_timestamp = 0; } virtual StateReturnType onEnter(); virtual void onExit( StateExitType status ); virtual StateReturnType update(); protected: // snapshot interface virtual void crc( Xfer *xfer ); virtual void xfer( Xfer *xfer ); virtual void loadPostProcess(); }; EMPTY_DTOR(TurretAIHoldTurretState) //------------------------------------------------------------------------------------------------- class TurretAIData : public MemoryPoolObject { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(TurretAIData, "TurretAIData") public: Real m_turnRate; Real m_pitchRate; Real m_naturalTurretAngle; Real m_naturalTurretPitch; Real m_turretFireAngleSweep[WEAPONSLOT_COUNT]; ///< if nonzero, sweep within +/- this angle range while firing Real m_turretSweepSpeedModifier[WEAPONSLOT_COUNT]; ///< While sweeping, change your speed by this Real m_firePitch; ///< if nonzero, I am on target at this fixed pitch, not when pointing at target Real m_minPitch; ///< dependent on allowspitch. defaults to 0 (horizontal). The lowest pitch allowed (negative to allow pointing down of a high turret) Real m_groundUnitPitch; ///< dependent on allowspitch. defaults to 0 (horizontal). The lowest pitch allowed when firing at ground units to give the weapon an arc. jba UnsignedInt m_turretWeaponSlots; ///< which WeaponSlots are controlled by this turret #ifdef INTER_TURRET_DELAY UnsignedInt m_interTurretDelay; ///< special-case for multiturret battleships #endif Real m_minIdleScanAngle; ///< max angle the turret can turn while idling Real m_maxIdleScanAngle; ///< max angle the turret can turn while idling UnsignedInt m_minIdleScanInterval; ///< min interval between idle scans UnsignedInt m_maxIdleScanInterval; ///< max interval between idle scans UnsignedInt m_recenterTime; ///< time to wait before recentering turret Bool m_initiallyDisabled; ///< manually controlled and disabled. Bool m_firesWhileTurning; ///< so the firing state does not instantly expire the turning state Bool m_isAllowsPitch; ///< This type of turret can pitch up and down as well as spin TurretAIData(); static void buildFieldParse(MultiIniFieldParse& p); static void parseTurretSweep(INI* ini, void *instance, void *store, const void* userData); static void parseTurretSweepSpeed(INI* ini, void *instance, void *store, const void* userData); }; EMPTY_DTOR(TurretAIData) //----------------------------------------------------------------------------------------------------- enum TurretTargetType { TARGET_NONE, TARGET_OBJECT, TARGET_POSITION }; //----------------------------------------------------------------------------------------------------- /** * Turret behavior implementation. */ class TurretAI : public MemoryPoolObject, public Snapshot, public NotifyWeaponFiredInterface { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( TurretAI, "TurretAI" ) public: TurretAI(Object* owner, const TurretAIData* data, WhichTurretType tur); // virtual destructor prototype provided by memory pool declaration Real getTurretAngle() const { return m_angle; } Real getTurretPitch() const { return m_pitch; } Real getMinPitch() const { return m_data->m_minPitch; } Bool isAllowsPitch() const { return m_data->m_isAllowsPitch; } Real getTurnRate() const { return m_data->m_turnRate; } Real getNaturalTurretAngle() const { return m_data->m_naturalTurretAngle; } Real getPitchRate() const { return m_data->m_pitchRate; } Real getFirePitch() const { return m_data->m_firePitch; } Real getGroundUnitPitch() const { return m_data->m_groundUnitPitch; } Real getNaturalTurretPitch() const { return m_data->m_naturalTurretPitch; } Real getTurretFireAngleSweepForWeaponSlot( WeaponSlotType slot ) const; Real getTurretSweepSpeedModifierForWeaponSlot( WeaponSlotType slot ) const; Real getMinIdleScanAngle() const { return m_data->m_minIdleScanAngle; } Real getMaxIdleScanAngle() const { return m_data->m_maxIdleScanAngle; } UnsignedInt getMinIdleScanInterval() const { return m_data->m_minIdleScanInterval; } UnsignedInt getMaxIdleScanInterval() const { return m_data->m_maxIdleScanInterval; } UnsignedInt getRecenterTime() const { return m_data->m_recenterTime; } Object* getOwner() { return m_owner; } const Object* getOwner() const { return m_owner; } Bool isOwnersCurWeaponOnTurret() const; Bool isWeaponSlotOnTurret(WeaponSlotType wslot) const; Bool isAttackingObject() const { return m_target == TARGET_OBJECT; } Bool isForceAttacking() const { return m_isForceAttacking; } // this will cause the turret to continuously track the given victim. // passing null will allow the turret to revert back to "normal" position. void setTurretTargetObject(Object* o, Bool forceAttacking); void setTurretTargetPosition(const Coord3D* pos); void recenterTurret(); Bool isTurretInNaturalPosition() const; void setTurretEnabled( Bool enabled ); Bool isTurretEnabled() const { return m_enabled; } /** return true iff the turret is trying to aim at the victim, BUT not yet pointing in the right dir. */ Bool isTryingToAimAtTarget(const Object* victim) const; UpdateSleepTime updateTurretAI(); ///< implement this module's behavior virtual void notifyFired(); virtual void notifyNewVictimChosen(Object* victim); virtual const Coord3D* getOriginalVictimPos() const { return NULL; } // yes, we return NULL here virtual Bool isWeaponSlotOkToFire(WeaponSlotType wslot) const; // these are only for use by the state machines... don't call them otherwise, please Bool friend_turnTowardsAngle(Real desiredAngle, Real rateModifier, Real relThresh); Bool friend_turnTowardsPitch(Real pitch, Real rateModifier); Bool friend_getPositiveSweep() const { return m_positiveSweep; } void friend_setPositiveSweep(Bool b) { m_positiveSweep = b; } Bool friend_isSweepEnabled() const; WhichTurretType friend_getWhichTurret() const { return m_whichTurret; } #ifdef INTER_TURRET_DELAY UnsignedInt friend_getInterTurretDelay(); #endif Bool friend_isAnyWeaponInRangeOf(const Object* o) const; TurretTargetType friend_getTurretTarget(Object*& obj, Coord3D& pos) const; Bool friend_getTargetWasSetByIdleMood() const { return m_targetWasSetByIdleMood; } const Team* friend_getVictimInitialTeam() const { return m_victimInitialTeam; } void friend_checkForIdleMoodTarget(); UnsignedInt friend_getNextIdleMoodTargetFrame() const; void friend_notifyStateMachineChanged(); protected: // snapshot interface virtual void crc( Xfer *xfer ); virtual void xfer( Xfer *xfer ); virtual void loadPostProcess(); private: void startRotOrPitchSound(); ///< start turret rotation sound void stopRotOrPitchSound(); ///< stop turret rotation sound void removeSelfAsTargeter(); #ifdef INTER_TURRET_DELAY void getOtherTurretWeaponInfo(Int& numSelf, Int& numSelfReloading, Int& numSelfReady, Int& numOther, Int& numOtherReloading, Int& numOtherReady) const; #endif const TurretAIData* m_data; const WhichTurretType m_whichTurret; Object* m_owner; TurretStateMachine* m_turretStateMachine; ///< the state machine Real m_angle; ///< angle of the turret Real m_pitch; ///< pitch angle, if this supports it AudioEventRTS m_turretRotOrPitchSound; ///< Sound of turret rotation UnsignedInt m_enableSweepUntil; Team* m_victimInitialTeam; // The team of the victim at the BEGINNING of the attack. If it changes, we may need to stop the attack. mutable TurretTargetType m_target; UnsignedInt m_continuousFireExpirationFrame; UnsignedInt m_sleepUntil; Bool m_playRotSound : 1; Bool m_playPitchSound : 1; Bool m_positiveSweep : 1; Bool m_didFire : 1; Bool m_enabled : 1; Bool m_firesWhileTurning : 1; Bool m_isForceAttacking : 1; mutable Bool m_targetWasSetByIdleMood : 1; }; #endif // end _TURRETAI_H_