text: #ifndef _INCLUDED_ASW_ROCKET_H
  #define _INCLUDED_ASW_ROCKET_H
  
  #include "basehlcombatweapon.h"
  
  class RocketTrail;
  
  /// This is a class that can handle cases where you have, say, ten projectiles
  /// and three enemies, and you'd like each enemy to get just the number of projectiles
  /// necessary to kill it. It stores a dictionary of targeted enemies, the total damage
  /// to be done so far upon each of them given the allocated projectiles, and a linked
  /// list of projectiles (or spell effects or what have you) allocated to each one. 
  class CASW_DamageAllocationMgr
  {
  public:
  		// typedefs
  	// relevant data about a given projectile: its handle, and the damage it causes
  	struct ProjectileInfo_t 
  	{	
  		EHANDLE m_hHandle;
  		float m_flDamage;
  
  		ProjectileInfo_t() : m_flDamage(0) {}
  		ProjectileInfo_t( CBaseEntity *pProj, float damage ) : m_hHandle(pProj),
  			m_flDamage(damage) {}
  	};
  
  	// we store the chain of projectiles allocated to each target 
  	// as a linked list. This is actually a MULTILIST, which means
  	// that many linked lists actually share the pool of spaces herein
  	typedef CUtlLinkedList< ProjectileInfo_t, unsigned short, true, unsigned int > ProjectilePool_t;
  
  	struct tuple_t 
  	{
  		EHANDLE m_hTargeted;
  		float   m_flAccumulatedDamage;
  		ProjectilePool_t::IndexType_t m_nProjectiles; ///< head of linked list of projectiles targeted at this object
  
  		tuple_t() : m_flAccumulatedDamage(0), m_nProjectiles( ProjectilePool_t::InvalidIndex() ) {};
  		tuple_t( const EHANDLE &handle ) : m_hTargeted(handle), 
  			m_flAccumulatedDamage(0), m_nProjectiles( ProjectilePool_t::InvalidIndex() ) {};
  	};
  
  	/// an index into the targets contained herein. 
  	// typedeffed just in case I need to change it from being a pointer.
  	typedef tuple_t* IndexType_t;
  
  		// functions
  	// get the element for a given IndexType_t
  	inline tuple_t &operator[]( const IndexType_t &I ) { return Elem(I); }
  	inline tuple_t &Elem( const IndexType_t &I );
  	inline bool IsValid( const IndexType_t &I ) const ;
  	static inline IndexType_t InvalidIndex();
  
  	// if the object hasn't got anything allocated, returns NULL
  	IndexType_t Find( const EHANDLE &handle ) ; 
  
  	// add this object to the damage allocator, with nothing allocated
  	// to it yet. returns the allocated tuple_t. returns the existing one
  	// if the object is already in the manager.
  	IndexType_t Insert( CBaseEntity* pTarget );
  
  	void Remove( const EHANDLE &handle );
  
  	// allocate a projectile to a target. returns total damage being done to target so far.
  	inline float Allocate(  CBaseEntity *pTarget, CBaseEntity *pProjectile, float flDamage );
  	float Allocate( IndexType_t iTarget, CBaseEntity *pProjectile, float flDamage );
  
  	// remove a projectile from a target. returns total damage being done to target so far.
  	inline float Deallocate(  CBaseEntity *pTarget, CBaseEntity *pProjectile );
  	float Deallocate( IndexType_t iTarget, CBaseEntity *pProjectile );
  
  	// is the given projectile allocated to the given target?
  	inline bool IsProjectileForTarget( IndexType_t iTarget, CBaseEntity *pProjectile );
  
  
  	// constructor
  	CASW_DamageAllocationMgr( int baseSize = 0 ) : m_Targets( 0, baseSize ), m_ProjectileLists( 0, baseSize ) {};
  
  protected:
  
  	// look through the projectile pool for a given target and return the index
  	// of the ProjectilePool_t corresponding to the given projectile. Return 
  	// ProjectilePool_t::InvalidIndex if not found.
  	ProjectilePool_t::IndexType_t GetProjectileIndexInTarget( IndexType_t iTarget, CBaseEntity *pProjectile );
  
  	// rebuild consistency
  	void Rebuild( IndexType_t iTarget );
  
  	CUtlVectorConservative< tuple_t > m_Targets;
  	ProjectilePool_t m_ProjectileLists;
  };
  
  inline CASW_DamageAllocationMgr::tuple_t & CASW_DamageAllocationMgr::Elem( const IndexType_t &I )
  {
  	return *I;
  }
  
  
  inline bool CASW_DamageAllocationMgr::IsProjectileForTarget( IndexType_t iTarget, CBaseEntity *pProjectile )
  {
  	return GetProjectileIndexInTarget( iTarget, pProjectile ) != ProjectilePool_t::InvalidIndex();
  }
  
  inline float CASW_DamageAllocationMgr::Allocate(  CBaseEntity *pTarget, CBaseEntity *pProjectile, float flDamage )
  {
  	IndexType_t i = Find(pTarget);
  	if ( !IsValid(i) )
  	{
  		AssertMsg2( false, "Tried to allocate %s as a damage projectile to %s but it isn't managed\n",
  			pProjectile->GetDebugName(), pTarget->GetDebugName() );
  		i = Insert( pTarget );
  	}
  	return Allocate( i, pProjectile, flDamage );
  }
  
  inline float CASW_DamageAllocationMgr::Deallocate(  CBaseEntity *pTarget, CBaseEntity *pProjectile )
  {
  	IndexType_t i = Find(pTarget);
  	if ( !IsValid(i) )
  	{
  		AssertMsg2( false, "Tried to remove %s as a damage projectile to unmanaged %s\n",
  			pProjectile->GetDebugName(), pTarget->GetDebugName() );
  		return 0;
  	}
  	else
  	{
  		return Deallocate( i, pProjectile );
  	}
  }
  
  inline CASW_DamageAllocationMgr::IndexType_t CASW_DamageAllocationMgr::InvalidIndex()
  {
  	return NULL;
  }
  
  inline bool CASW_DamageAllocationMgr::IsValid( const IndexType_t &I ) const
  {
  	return I != InvalidIndex();
  }
  
  class CASW_Rocket : public CBaseCombatCharacter
  {
  	DECLARE_CLASS( CASW_Rocket, CBaseCombatCharacter );
  	DECLARE_SERVERCLASS();
  public:
  	static const int EXPLOSION_RADIUS = 200;
  
  	CASW_Rocket();
  	virtual ~CASW_Rocket();
  
  	Class_T Classify( void ) { return CLASS_MISSILE; }
  	
  	virtual void Spawn( void );
  	virtual void Activate( void );
  	virtual void StopLoopingSounds( void );
  	virtual void Precache( void );
  
  	void	MissileTouch( CBaseEntity *pOther );
  	void	Explode( void );
  	void	AccelerateThink( void );
  	void	IgniteThink( void );
  	void	SeekThink( void );
  	void	DumbFire( void );
  	void	SetGracePeriod( float flGracePeriod );
  
  	int		OnTakeDamage_Alive( const CTakeDamageInfo &info );
  	void	Event_Killed( const CTakeDamageInfo &info );
  	
  	virtual float	GetDamage() { return m_flDamage; }
  	virtual void	SetDamage(float flDamage) { m_flDamage = flDamage; }
  
  	unsigned int PhysicsSolidMaskForEntity( void ) const;	
  
  	static CASW_Rocket *Create( float fDamage, const Vector &vecOrigin, const QAngle &vecAngles, CBaseCombatCharacter *pentOwner = NULL , CBaseEntity * pCreatorWeapon = NULL, const char *className = "asw_rocket" );
  
  	void CreateDangerSounds( bool bState ){ m_bCreateDangerSounds = bState; }
  
  	void FindHomingPosition( Vector *pTarget );
  	CBaseEntity *FindPotentialTarget( void ) const ; /// find a good enemy for us to target. The enemy isn't actually targeted until the result is written back to m_hHomingTarget
  	void SetTarget( CBaseEntity *pTarget );
  	inline CBaseEntity *GetTarget( void )  { return  m_hHomingTarget.Get(); }
  
  	virtual void DrawDebugGeometryOverlays();
  
  	int		m_iAttributeEffects;
  
  	EHANDLE m_hCreatorWeapon;
  	Class_T m_CreatorWeaponClass;
  
  protected:
  	virtual void DoExplosion(bool bHitwall);
  	Vector		 IntegrateRocketThrust( const Vector &vTargetDir, float flDist ) const ;
  	void		 ComputeWallDodge( const Vector &vCurVel ); /// if we're headed for a wall, adjust the wobble angles so we steer away
  
  	float		 GetLifeFraction() const;
  
  	bool IsWallDodging() const;
  
  	EHANDLE m_hHomingTarget;
  
  	float m_flDamage;
  	float m_fSpawnTime;
  	float m_fDieTime;
  
  	const char *			m_szFlightSound;
  	const char *			m_szDetonationSound;
  
  	static CASW_DamageAllocationMgr m_RocketAssigner;
  
  private:
  	float					m_flGracePeriodEndsAt;
  	float					m_flNextWobbleTime;
  
  	QAngle					m_vWobbleAngles; ///< will approach this direction while wobbling
  
  	bool					m_bCreateDangerSounds;
  	bool					m_bFlyingWild; // has no target, is wobbling crazily
  
  	DECLARE_DATADESC();
  };
  
  #endif // _INCLUDED_ASW_ROCKET_H