379 lines
15 KiB
C++
379 lines
15 KiB
C++
/*
|
|
** 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// (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_
|
|
|