Portable Half-Life SDK. GoldSource and Xash3D. Crossplatform.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1021 lines
26 KiB

9 years ago
/***
*
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* Use, distribution, and modification of this source code and/or resulting
* object code is restricted to non-commercial enhancements to products from
* Valve LLC. All other use, distribution, or modification is prohibited
* without written permission from Valve LLC.
*
****/
9 years ago
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "effects.h"
#include "weapons.h"
#include "explode.h"
#include "player.h"
#define SF_TANK_ACTIVE 0x0001
#define SF_TANK_PLAYER 0x0002
#define SF_TANK_HUMANS 0x0004
#define SF_TANK_ALIENS 0x0008
#define SF_TANK_LINEOFSIGHT 0x0010
#define SF_TANK_CANCONTROL 0x0020
#define SF_TANK_SOUNDON 0x8000
enum TANKBULLET
{
TANK_BULLET_NONE = 0,
TANK_BULLET_9MM = 1,
TANK_BULLET_MP5 = 2,
TANK_BULLET_12MM = 3
9 years ago
};
// Custom damage
// env_laser (duration is 0.5 rate of fire)
// rockets
// explosion?
class CFuncTank : public CBaseEntity
{
public:
9 years ago
void Spawn( void );
void Precache( void );
void KeyValue( KeyValueData *pkvd );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void Think( void );
void TrackTarget( void );
9 years ago
virtual void Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker );
virtual Vector UpdateTargetPosition( CBaseEntity *pTarget )
{
return pTarget->BodyTarget( pev->origin );
}
9 years ago
void StartRotSound( void );
void StopRotSound( void );
9 years ago
// Bmodels don't go across transitions
9 years ago
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
9 years ago
inline BOOL IsActive( void ) { return (pev->spawnflags & SF_TANK_ACTIVE)?TRUE:FALSE; }
inline void TankActivate( void ) { pev->spawnflags |= SF_TANK_ACTIVE; pev->nextthink = pev->ltime + 0.1; m_fireLast = 0; }
inline void TankDeactivate( void ) { pev->spawnflags &= ~SF_TANK_ACTIVE; m_fireLast = 0; StopRotSound(); }
inline BOOL CanFire( void ) { return (gpGlobals->time - m_lastSightTime) < m_persist; }
9 years ago
BOOL InRange( float range );
9 years ago
// Acquire a target. pPlayer is a player in the PVS
edict_t *FindTarget( edict_t *pPlayer );
void TankTrace( const Vector &vecStart, const Vector &vecForward, const Vector &vecSpread, TraceResult &tr );
Vector BarrelPosition( void )
{
Vector forward, right, up;
UTIL_MakeVectorsPrivate( pev->angles, forward, right, up );
return pev->origin + (forward * m_barrelPos.x) + (right * m_barrelPos.y) + (up * m_barrelPos.z);
}
void AdjustAnglesForBarrel( Vector &angles, float distance );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
BOOL OnControls( entvars_t *pevTest );
BOOL StartControl( CBasePlayer* pController );
void StopControl( void );
void ControllerPostFrame( void );
virtual void StopFire( void ){}
9 years ago
protected:
CBasePlayer* m_pController;
float m_flNextAttack;
Vector m_vecControllerUsePos;
float m_yawCenter; // "Center" yaw
float m_yawRate; // Max turn rate to track targets
float m_yawRange; // Range of turning motion (one-sided: 30 is +/- 30 degress from center)
// Zero is full rotation
float m_yawTolerance; // Tolerance angle
float m_pitchCenter; // "Center" pitch
float m_pitchRate; // Max turn rate on pitch
float m_pitchRange; // Range of pitch motion as above
float m_pitchTolerance; // Tolerance angle
float m_fireLast; // Last time I fired
float m_fireRate; // How many rounds/second
float m_lastSightTime;// Last time I saw target
float m_persist; // Persistence of firing (how long do I shoot when I can't see)
float m_minRange; // Minimum range to aim/track
float m_maxRange; // Max range to aim/track
Vector m_barrelPos; // Length of the freakin barrel
float m_spriteScale; // Scale of any sprites we shoot
string_t m_iszSpriteSmoke;
string_t m_iszSpriteFlash;
9 years ago
TANKBULLET m_bulletType; // Bullet type
int m_iBulletDamage; // 0 means use Bullet type's default damage
Vector m_sightOrigin; // Last sight of target
int m_spread; // firing spread
string_t m_iszMaster; // Master entity (game_team_master or multisource)
9 years ago
};
9 years ago
TYPEDESCRIPTION CFuncTank::m_SaveData[] =
9 years ago
{
DEFINE_FIELD( CFuncTank, m_yawCenter, FIELD_FLOAT ),
DEFINE_FIELD( CFuncTank, m_yawRate, FIELD_FLOAT ),
DEFINE_FIELD( CFuncTank, m_yawRange, FIELD_FLOAT ),
DEFINE_FIELD( CFuncTank, m_yawTolerance, FIELD_FLOAT ),
DEFINE_FIELD( CFuncTank, m_pitchCenter, FIELD_FLOAT ),
DEFINE_FIELD( CFuncTank, m_pitchRate, FIELD_FLOAT ),
DEFINE_FIELD( CFuncTank, m_pitchRange, FIELD_FLOAT ),
DEFINE_FIELD( CFuncTank, m_pitchTolerance, FIELD_FLOAT ),
DEFINE_FIELD( CFuncTank, m_fireLast, FIELD_TIME ),
DEFINE_FIELD( CFuncTank, m_fireRate, FIELD_FLOAT ),
DEFINE_FIELD( CFuncTank, m_lastSightTime, FIELD_TIME ),
DEFINE_FIELD( CFuncTank, m_persist, FIELD_FLOAT ),
DEFINE_FIELD( CFuncTank, m_minRange, FIELD_FLOAT ),
DEFINE_FIELD( CFuncTank, m_maxRange, FIELD_FLOAT ),
DEFINE_FIELD( CFuncTank, m_barrelPos, FIELD_VECTOR ),
DEFINE_FIELD( CFuncTank, m_spriteScale, FIELD_FLOAT ),
DEFINE_FIELD( CFuncTank, m_iszSpriteSmoke, FIELD_STRING ),
DEFINE_FIELD( CFuncTank, m_iszSpriteFlash, FIELD_STRING ),
DEFINE_FIELD( CFuncTank, m_bulletType, FIELD_INTEGER ),
DEFINE_FIELD( CFuncTank, m_sightOrigin, FIELD_VECTOR ),
DEFINE_FIELD( CFuncTank, m_spread, FIELD_INTEGER ),
DEFINE_FIELD( CFuncTank, m_pController, FIELD_CLASSPTR ),
DEFINE_FIELD( CFuncTank, m_vecControllerUsePos, FIELD_VECTOR ),
DEFINE_FIELD( CFuncTank, m_flNextAttack, FIELD_TIME ),
DEFINE_FIELD( CFuncTank, m_iBulletDamage, FIELD_INTEGER ),
DEFINE_FIELD( CFuncTank, m_iszMaster, FIELD_STRING ),
};
IMPLEMENT_SAVERESTORE( CFuncTank, CBaseEntity )
9 years ago
static Vector gTankSpread[] =
{
Vector( 0, 0, 0 ), // perfect
Vector( 0.025, 0.025, 0.025 ), // small cone
Vector( 0.05, 0.05, 0.05 ), // medium cone
Vector( 0.1, 0.1, 0.1 ), // large cone
Vector( 0.25, 0.25, 0.25 ), // extra-large cone
};
9 years ago
#define MAX_FIRING_SPREADS ARRAYSIZE( gTankSpread )
9 years ago
9 years ago
void CFuncTank::Spawn( void )
9 years ago
{
Precache();
9 years ago
pev->movetype = MOVETYPE_PUSH; // so it doesn't get pushed by anything
pev->solid = SOLID_BSP;
SET_MODEL( ENT( pev ), STRING( pev->model ) );
9 years ago
m_yawCenter = pev->angles.y;
m_pitchCenter = pev->angles.x;
9 years ago
if( IsActive() )
9 years ago
pev->nextthink = pev->ltime + 1.0;
m_sightOrigin = BarrelPosition(); // Point at the end of the barrel
9 years ago
if( m_fireRate <= 0 )
9 years ago
m_fireRate = 1;
if( m_spread > (int)MAX_FIRING_SPREADS )
9 years ago
m_spread = 0;
pev->oldorigin = pev->origin;
}
9 years ago
void CFuncTank::Precache( void )
9 years ago
{
9 years ago
if( m_iszSpriteSmoke )
PRECACHE_MODEL( STRING( m_iszSpriteSmoke ) );
9 years ago
9 years ago
if( m_iszSpriteFlash )
PRECACHE_MODEL( STRING( m_iszSpriteFlash ) );
9 years ago
if( pev->noise )
PRECACHE_SOUND( STRING( pev->noise ) );
9 years ago
}
9 years ago
void CFuncTank::KeyValue( KeyValueData *pkvd )
9 years ago
{
9 years ago
if( FStrEq( pkvd->szKeyName, "yawrate" ) )
9 years ago
{
9 years ago
m_yawRate = atof( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
9 years ago
else if( FStrEq( pkvd->szKeyName, "yawrange" ) )
9 years ago
{
9 years ago
m_yawRange = atof( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
9 years ago
else if( FStrEq( pkvd->szKeyName, "yawtolerance" ) )
9 years ago
{
9 years ago
m_yawTolerance = atof( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
9 years ago
else if( FStrEq( pkvd->szKeyName, "pitchrange" ) )
9 years ago
{
9 years ago
m_pitchRange = atof( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
9 years ago
else if( FStrEq( pkvd->szKeyName, "pitchrate" ) )
9 years ago
{
9 years ago
m_pitchRate = atof( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
9 years ago
else if( FStrEq( pkvd->szKeyName, "pitchtolerance" ) )
9 years ago
{
9 years ago
m_pitchTolerance = atof( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
9 years ago
else if( FStrEq( pkvd->szKeyName, "firerate" ) )
9 years ago
{
9 years ago
m_fireRate = atof( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
9 years ago
else if( FStrEq( pkvd->szKeyName, "barrel" ) )
9 years ago
{
9 years ago
m_barrelPos.x = atof( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
9 years ago
else if( FStrEq( pkvd->szKeyName, "barrely" ) )
9 years ago
{
9 years ago
m_barrelPos.y = atof( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
9 years ago
else if( FStrEq( pkvd->szKeyName, "barrelz" ) )
9 years ago
{
9 years ago
m_barrelPos.z = atof( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
9 years ago
else if( FStrEq( pkvd->szKeyName, "spritescale" ) )
9 years ago
{
9 years ago
m_spriteScale = atof( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
9 years ago
else if( FStrEq( pkvd->szKeyName, "spritesmoke" ) )
9 years ago
{
9 years ago
m_iszSpriteSmoke = ALLOC_STRING( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
9 years ago
else if( FStrEq( pkvd->szKeyName, "spriteflash" ) )
9 years ago
{
9 years ago
m_iszSpriteFlash = ALLOC_STRING( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
9 years ago
else if( FStrEq( pkvd->szKeyName, "rotatesound" ) )
9 years ago
{
9 years ago
pev->noise = ALLOC_STRING( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
9 years ago
else if( FStrEq( pkvd->szKeyName, "persistence" ) )
9 years ago
{
9 years ago
m_persist = atof( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
9 years ago
else if( FStrEq( pkvd->szKeyName, "bullet" ) )
9 years ago
{
9 years ago
m_bulletType = (TANKBULLET)atoi( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
9 years ago
else if( FStrEq( pkvd->szKeyName, "bullet_damage" ) )
9 years ago
{
9 years ago
m_iBulletDamage = atoi( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
9 years ago
else if( FStrEq(pkvd->szKeyName, "firespread" ) )
9 years ago
{
9 years ago
m_spread = atoi( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
9 years ago
else if( FStrEq( pkvd->szKeyName, "minRange" ) )
9 years ago
{
9 years ago
m_minRange = atof( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
9 years ago
else if( FStrEq( pkvd->szKeyName, "maxRange" ) )
9 years ago
{
9 years ago
m_maxRange = atof( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
9 years ago
else if( FStrEq( pkvd->szKeyName, "master" ) )
9 years ago
{
9 years ago
m_iszMaster = ALLOC_STRING( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
else
CBaseEntity::KeyValue( pkvd );
}
////////////// START NEW STUFF //////////////
//==================================================================================
// TANK CONTROLLING
9 years ago
BOOL CFuncTank::OnControls( entvars_t *pevTest )
9 years ago
{
9 years ago
if( !( pev->spawnflags & SF_TANK_CANCONTROL ) )
9 years ago
return FALSE;
//Vector offset = pevTest->origin - pev->origin;
9 years ago
9 years ago
if( ( m_vecControllerUsePos - pevTest->origin ).Length() < 30 )
9 years ago
return TRUE;
return FALSE;
}
9 years ago
BOOL CFuncTank::StartControl( CBasePlayer *pController )
9 years ago
{
9 years ago
if( m_pController != NULL )
9 years ago
return FALSE;
// Team only or disabled?
9 years ago
if( m_iszMaster )
9 years ago
{
9 years ago
if( !UTIL_IsMasterTriggered( m_iszMaster, pController ) )
9 years ago
return FALSE;
}
ALERT( at_console, "using TANK!\n");
m_pController = pController;
m_pController->m_pTank = this;
9 years ago
if( m_pController->m_pActiveItem )
9 years ago
{
m_pController->m_pActiveItem->Holster();
m_pController->pev->weaponmodel = 0;
9 years ago
m_pController->pev->viewmodel = 0;
9 years ago
}
m_pController->m_iHideHUD |= HIDEHUD_WEAPONS;
m_vecControllerUsePos = m_pController->pev->origin;
9 years ago
9 years ago
pev->nextthink = pev->ltime + 0.1;
9 years ago
9 years ago
return TRUE;
}
9 years ago
void CFuncTank::StopControl()
9 years ago
{
StopFire();
9 years ago
// TODO: bring back the controllers current weapon
9 years ago
if( !m_pController )
9 years ago
return;
9 years ago
if( m_pController->m_pActiveItem )
9 years ago
m_pController->m_pActiveItem->Deploy();
ALERT( at_console, "stopped using TANK\n");
m_pController->m_iHideHUD &= ~HIDEHUD_WEAPONS;
pev->nextthink = 0;
m_pController->m_pTank = NULL;
9 years ago
m_pController = NULL;
9 years ago
if( IsActive() )
9 years ago
pev->nextthink = pev->ltime + 1.0;
}
// Called each frame by the player's ItemPostFrame
9 years ago
void CFuncTank::ControllerPostFrame( void )
9 years ago
{
9 years ago
ASSERT( m_pController != NULL );
9 years ago
9 years ago
if( gpGlobals->time < m_flNextAttack )
9 years ago
return;
9 years ago
if( m_pController->pev->button & IN_ATTACK )
9 years ago
{
Vector vecForward;
UTIL_MakeVectorsPrivate( pev->angles, vecForward, NULL, NULL );
9 years ago
m_fireLast = gpGlobals->time - ( 1 / m_fireRate ) - 0.01; // to make sure the gun doesn't fire too many bullets
9 years ago
Fire( BarrelPosition(), vecForward, m_pController->pev );
// HACKHACK -- make some noise (that the AI can hear)
9 years ago
if( m_pController && m_pController->IsPlayer() )
( (CBasePlayer *)m_pController )->m_iWeaponVolume = LOUD_GUN_VOLUME;
9 years ago
9 years ago
m_flNextAttack = gpGlobals->time + ( 1 / m_fireRate );
9 years ago
}
}
////////////// END NEW STUFF //////////////
9 years ago
void CFuncTank::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
9 years ago
{
9 years ago
if( pev->spawnflags & SF_TANK_CANCONTROL )
{
// player controlled turret
if( pActivator->Classify() != CLASS_PLAYER )
9 years ago
return;
9 years ago
if( value == 2 && useType == USE_SET )
9 years ago
{
ControllerPostFrame();
}
9 years ago
else if( !m_pController && useType != USE_OFF )
9 years ago
{
StartControl( (CBasePlayer*)pActivator );
}
else
{
StopControl();
}
}
else
{
9 years ago
if( !ShouldToggle( useType, IsActive() ) )
9 years ago
return;
9 years ago
if( IsActive() )
9 years ago
TankDeactivate();
else
TankActivate();
}
}
9 years ago
edict_t *CFuncTank::FindTarget( edict_t *pPlayer )
9 years ago
{
return pPlayer;
}
9 years ago
BOOL CFuncTank::InRange( float range )
9 years ago
{
9 years ago
if( range < m_minRange )
9 years ago
return FALSE;
9 years ago
if( m_maxRange > 0 && range > m_maxRange )
9 years ago
return FALSE;
return TRUE;
}
9 years ago
void CFuncTank::Think( void )
9 years ago
{
pev->avelocity = g_vecZero;
TrackTarget();
9 years ago
if( fabs( pev->avelocity.x ) > 1 || fabs( pev->avelocity.y ) > 1 )
9 years ago
StartRotSound();
else
StopRotSound();
}
void CFuncTank::TrackTarget( void )
{
TraceResult tr;
edict_t *pPlayer = FIND_CLIENT_IN_PVS( edict() );
BOOL updateTime = FALSE;
9 years ago
Vector angles, direction, targetPosition, barrelEnd;
edict_t *pTarget = NULL;
9 years ago
// Get a position to aim for
9 years ago
if( m_pController )
9 years ago
{
// Tanks attempt to mirror the player's angles
angles = m_pController->pev->v_angle;
angles[0] = 0 - angles[0];
pev->nextthink = pev->ltime + 0.05;
}
else
{
9 years ago
if( IsActive() )
9 years ago
pev->nextthink = pev->ltime + 0.1;
else
return;
9 years ago
if( FNullEnt( pPlayer ) )
9 years ago
{
9 years ago
if( IsActive() )
9 years ago
pev->nextthink = pev->ltime + 2; // Wait 2 secs
return;
}
pTarget = FindTarget( pPlayer );
9 years ago
if( !pTarget )
9 years ago
return;
// Calculate angle needed to aim at target
barrelEnd = BarrelPosition();
targetPosition = pTarget->v.origin + pTarget->v.view_ofs;
9 years ago
float range = ( targetPosition - barrelEnd ).Length();
if( !InRange( range ) )
9 years ago
return;
UTIL_TraceLine( barrelEnd, targetPosition, dont_ignore_monsters, edict(), &tr );
9 years ago
if( tr.flFraction == 1.0 || tr.pHit == pTarget )
9 years ago
{
CBaseEntity *pInstance = CBaseEntity::Instance(pTarget);
9 years ago
if( InRange( range ) && pInstance && pInstance->IsAlive() )
9 years ago
{
updateTime = TRUE;
m_sightOrigin = UpdateTargetPosition( pInstance );
}
}
// Track sight origin
// !!! I'm not sure what i changed
9 years ago
direction = m_sightOrigin - pev->origin;
//direction = m_sightOrigin - barrelEnd;
9 years ago
angles = UTIL_VecToAngles( direction );
// Calculate the additional rotation to point the end of the barrel at the target (not the gun's center)
AdjustAnglesForBarrel( angles, direction.Length() );
}
angles.x = -angles.x;
// Force the angles to be relative to the center position
angles.y = m_yawCenter + UTIL_AngleDistance( angles.y, m_yawCenter );
angles.x = m_pitchCenter + UTIL_AngleDistance( angles.x, m_pitchCenter );
// Limit against range in y
9 years ago
if( angles.y > m_yawCenter + m_yawRange )
9 years ago
{
angles.y = m_yawCenter + m_yawRange;
updateTime = FALSE; // Don't update if you saw the player, but out of range
}
9 years ago
else if( angles.y < ( m_yawCenter - m_yawRange ) )
9 years ago
{
9 years ago
angles.y = ( m_yawCenter - m_yawRange );
9 years ago
updateTime = FALSE; // Don't update if you saw the player, but out of range
}
9 years ago
if( updateTime )
9 years ago
m_lastSightTime = gpGlobals->time;
// Move toward target at rate or less
float distY = UTIL_AngleDistance( angles.y, pev->angles.y );
pev->avelocity.y = distY * 10;
9 years ago
if( pev->avelocity.y > m_yawRate )
9 years ago
pev->avelocity.y = m_yawRate;
9 years ago
else if( pev->avelocity.y < -m_yawRate )
9 years ago
pev->avelocity.y = -m_yawRate;
// Limit against range in x
9 years ago
if( angles.x > m_pitchCenter + m_pitchRange )
9 years ago
angles.x = m_pitchCenter + m_pitchRange;
9 years ago
else if( angles.x < m_pitchCenter - m_pitchRange )
9 years ago
angles.x = m_pitchCenter - m_pitchRange;
// Move toward target at rate or less
float distX = UTIL_AngleDistance( angles.x, pev->angles.x );
pev->avelocity.x = distX * 10;
9 years ago
if( pev->avelocity.x > m_pitchRate )
9 years ago
pev->avelocity.x = m_pitchRate;
9 years ago
else if( pev->avelocity.x < -m_pitchRate )
9 years ago
pev->avelocity.x = -m_pitchRate;
9 years ago
if( m_pController )
9 years ago
return;
9 years ago
if( CanFire() && ( ( fabs( distX ) < m_pitchTolerance && fabs( distY ) < m_yawTolerance ) || ( pev->spawnflags & SF_TANK_LINEOFSIGHT ) ) )
9 years ago
{
BOOL fire = FALSE;
Vector forward;
UTIL_MakeVectorsPrivate( pev->angles, forward, NULL, NULL );
9 years ago
if( pev->spawnflags & SF_TANK_LINEOFSIGHT )
9 years ago
{
float length = direction.Length();
UTIL_TraceLine( barrelEnd, barrelEnd + forward * length, dont_ignore_monsters, edict(), &tr );
9 years ago
if( tr.pHit == pTarget )
9 years ago
fire = TRUE;
}
else
fire = TRUE;
9 years ago
if( fire )
9 years ago
{
Fire( BarrelPosition(), forward, pev );
}
else
m_fireLast = 0;
}
else
m_fireLast = 0;
}
// If barrel is offset, add in additional rotation
void CFuncTank::AdjustAnglesForBarrel( Vector &angles, float distance )
{
float r2, d2;
9 years ago
if( m_barrelPos.y != 0 || m_barrelPos.z != 0 )
9 years ago
{
distance -= m_barrelPos.z;
d2 = distance * distance;
9 years ago
if( m_barrelPos.y )
9 years ago
{
r2 = m_barrelPos.y * m_barrelPos.y;
9 years ago
angles.y += ( 180.0 / M_PI ) * atan2( m_barrelPos.y, sqrt( d2 - r2 ) );
9 years ago
}
9 years ago
if( m_barrelPos.z )
9 years ago
{
r2 = m_barrelPos.z * m_barrelPos.z;
9 years ago
angles.x += ( 180.0 / M_PI ) * atan2( -m_barrelPos.z, sqrt( d2 - r2 ) );
9 years ago
}
}
}
// Fire targets and spawn sprites
void CFuncTank::Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker )
{
9 years ago
if( m_fireLast != 0 )
9 years ago
{
9 years ago
if( m_iszSpriteSmoke )
9 years ago
{
9 years ago
CSprite *pSprite = CSprite::SpriteCreate( STRING( m_iszSpriteSmoke ), barrelEnd, TRUE );
9 years ago
pSprite->AnimateAndDie( RANDOM_FLOAT( 15.0, 20.0 ) );
pSprite->SetTransparency( kRenderTransAlpha, (int)pev->rendercolor.x, (int)pev->rendercolor.y, (int)pev->rendercolor.z, 255, kRenderFxNone );
9 years ago
pSprite->pev->velocity.z = RANDOM_FLOAT( 40, 80 );
9 years ago
pSprite->SetScale( m_spriteScale );
}
9 years ago
if( m_iszSpriteFlash )
9 years ago
{
9 years ago
CSprite *pSprite = CSprite::SpriteCreate( STRING( m_iszSpriteFlash ), barrelEnd, TRUE );
9 years ago
pSprite->AnimateAndDie( 60 );
pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNoDissipation );
pSprite->SetScale( m_spriteScale );
// Hack Hack, make it stick around for at least 100 ms.
pSprite->pev->nextthink += 0.1;
}
SUB_UseTargets( this, USE_TOGGLE, 0 );
}
m_fireLast = gpGlobals->time;
}
void CFuncTank::TankTrace( const Vector &vecStart, const Vector &vecForward, const Vector &vecSpread, TraceResult &tr )
{
// get circular gaussian spread
float x, y, z;
9 years ago
do
{
x = RANDOM_FLOAT( -0.5, 0.5 ) + RANDOM_FLOAT( -0.5, 0.5 );
y = RANDOM_FLOAT( -0.5, 0.5 ) + RANDOM_FLOAT( -0.5, 0.5 );
z = x * x + y * y;
} while( z > 1 );
9 years ago
Vector vecDir = vecForward +
x * vecSpread.x * gpGlobals->v_right +
y * vecSpread.y * gpGlobals->v_up;
Vector vecEnd;
9 years ago
9 years ago
vecEnd = vecStart + vecDir * 4096;
UTIL_TraceLine( vecStart, vecEnd, dont_ignore_monsters, edict(), &tr );
}
void CFuncTank::StartRotSound( void )
{
9 years ago
if( !pev->noise || ( pev->spawnflags & SF_TANK_SOUNDON ) )
9 years ago
return;
pev->spawnflags |= SF_TANK_SOUNDON;
EMIT_SOUND( edict(), CHAN_STATIC, STRING( pev->noise ), 0.85, ATTN_NORM );
9 years ago
}
void CFuncTank::StopRotSound( void )
{
9 years ago
if( pev->spawnflags & SF_TANK_SOUNDON )
STOP_SOUND( edict(), CHAN_STATIC, STRING( pev->noise ) );
9 years ago
pev->spawnflags &= ~SF_TANK_SOUNDON;
}
class CFuncTankGun : public CFuncTank
{
public:
void Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker );
};
LINK_ENTITY_TO_CLASS( func_tank, CFuncTankGun )
9 years ago
void CFuncTankGun::Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker )
{
int i;
9 years ago
if( m_fireLast != 0 )
9 years ago
{
// FireBullets needs gpGlobals->v_up, etc.
9 years ago
UTIL_MakeAimVectors( pev->angles );
9 years ago
int bulletCount = (int)( ( gpGlobals->time - m_fireLast ) * m_fireRate );
9 years ago
if( bulletCount > 0 )
9 years ago
{
9 years ago
for( i = 0; i < bulletCount; i++ )
9 years ago
{
switch( m_bulletType )
{
case TANK_BULLET_9MM:
FireBullets( 1, barrelEnd, forward, gTankSpread[m_spread], 4096, BULLET_MONSTER_9MM, 1, m_iBulletDamage, pevAttacker );
break;
case TANK_BULLET_MP5:
FireBullets( 1, barrelEnd, forward, gTankSpread[m_spread], 4096, BULLET_MONSTER_MP5, 1, m_iBulletDamage, pevAttacker );
break;
case TANK_BULLET_12MM:
FireBullets( 1, barrelEnd, forward, gTankSpread[m_spread], 4096, BULLET_MONSTER_12MM, 1, m_iBulletDamage, pevAttacker );
break;
default:
case TANK_BULLET_NONE:
break;
}
}
CFuncTank::Fire( barrelEnd, forward, pevAttacker );
}
}
else
CFuncTank::Fire( barrelEnd, forward, pevAttacker );
}
class CFuncTankLaser : public CFuncTank
{
public:
9 years ago
void Activate( void );
void KeyValue( KeyValueData *pkvd );
void Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker );
void Think( void );
9 years ago
CLaser *GetLaser( void );
9 years ago
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
virtual void StopFire( void );
9 years ago
private:
9 years ago
CLaser *m_pLaser;
float m_laserTime;
9 years ago
};
LINK_ENTITY_TO_CLASS( func_tanklaser, CFuncTankLaser )
9 years ago
9 years ago
TYPEDESCRIPTION CFuncTankLaser::m_SaveData[] =
9 years ago
{
DEFINE_FIELD( CFuncTankLaser, m_pLaser, FIELD_CLASSPTR ),
DEFINE_FIELD( CFuncTankLaser, m_laserTime, FIELD_TIME ),
};
IMPLEMENT_SAVERESTORE( CFuncTankLaser, CFuncTank )
9 years ago
void CFuncTankLaser::Activate( void )
{
9 years ago
if( !GetLaser() )
9 years ago
{
9 years ago
UTIL_Remove( this );
9 years ago
ALERT( at_error, "Laser tank with no env_laser!\n" );
}
else
{
m_pLaser->TurnOff();
}
}
void CFuncTankLaser::KeyValue( KeyValueData *pkvd )
{
9 years ago
if( FStrEq( pkvd->szKeyName, "laserentity" ) )
9 years ago
{
9 years ago
pev->message = ALLOC_STRING( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
else
CFuncTank::KeyValue( pkvd );
}
CLaser *CFuncTankLaser::GetLaser( void )
{
9 years ago
if( m_pLaser )
9 years ago
return m_pLaser;
edict_t *pentLaser;
9 years ago
pentLaser = FIND_ENTITY_BY_TARGETNAME( NULL, STRING( pev->message ) );
while( !FNullEnt( pentLaser ) )
9 years ago
{
// Found the landmark
9 years ago
if( FClassnameIs( pentLaser, "env_laser" ) )
9 years ago
{
9 years ago
m_pLaser = (CLaser *)CBaseEntity::Instance( pentLaser );
9 years ago
break;
}
else
9 years ago
pentLaser = FIND_ENTITY_BY_TARGETNAME( pentLaser, STRING( pev->message ) );
9 years ago
}
return m_pLaser;
}
void CFuncTankLaser::Think( void )
{
9 years ago
if( m_pLaser && (gpGlobals->time > m_laserTime) )
9 years ago
m_pLaser->TurnOff();
CFuncTank::Think();
}
void CFuncTankLaser::Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker )
{
int i;
TraceResult tr;
9 years ago
if( m_fireLast != 0 && GetLaser() )
9 years ago
{
// TankTrace needs gpGlobals->v_up, etc.
9 years ago
UTIL_MakeAimVectors( pev->angles );
9 years ago
int bulletCount = (int)( ( gpGlobals->time - m_fireLast ) * m_fireRate );
9 years ago
if( bulletCount )
9 years ago
{
9 years ago
for( i = 0; i < bulletCount; i++ )
9 years ago
{
m_pLaser->pev->origin = barrelEnd;
TankTrace( barrelEnd, forward, gTankSpread[m_spread], tr );
9 years ago
9 years ago
m_laserTime = gpGlobals->time;
m_pLaser->TurnOn();
m_pLaser->pev->dmgtime = gpGlobals->time - 1.0;
m_pLaser->FireAtPoint( tr );
m_pLaser->pev->nextthink = 0;
}
CFuncTank::Fire( barrelEnd, forward, pev );
}
}
else
{
CFuncTank::Fire( barrelEnd, forward, pev );
}
}
void CFuncTankLaser::StopFire( void )
{
if( m_pLaser )
m_pLaser->TurnOff();
}
9 years ago
class CFuncTankRocket : public CFuncTank
{
public:
void Precache( void );
void Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker );
};
LINK_ENTITY_TO_CLASS( func_tankrocket, CFuncTankRocket )
9 years ago
void CFuncTankRocket::Precache( void )
{
UTIL_PrecacheOther( "rpg_rocket" );
CFuncTank::Precache();
}
void CFuncTankRocket::Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker )
{
int i;
9 years ago
if( m_fireLast != 0 )
9 years ago
{
int bulletCount = (int)( ( gpGlobals->time - m_fireLast ) * m_fireRate );
9 years ago
if( bulletCount > 0 )
9 years ago
{
9 years ago
for( i = 0; i < bulletCount; i++ )
9 years ago
{
CBaseEntity::Create( "rpg_rocket", barrelEnd, pev->angles, edict() );
9 years ago
}
CFuncTank::Fire( barrelEnd, forward, pev );
}
}
else
CFuncTank::Fire( barrelEnd, forward, pev );
}
class CFuncTankMortar : public CFuncTank
{
public:
void KeyValue( KeyValueData *pkvd );
void Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker );
};
LINK_ENTITY_TO_CLASS( func_tankmortar, CFuncTankMortar )
9 years ago
void CFuncTankMortar::KeyValue( KeyValueData *pkvd )
{
9 years ago
if( FStrEq( pkvd->szKeyName, "iMagnitude" ) )
9 years ago
{
pev->impulse = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else
CFuncTank::KeyValue( pkvd );
}
void CFuncTankMortar::Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker )
{
9 years ago
if( m_fireLast != 0 )
9 years ago
{
int bulletCount = (int)( ( gpGlobals->time - m_fireLast ) * m_fireRate );
9 years ago
// Only create 1 explosion
9 years ago
if( bulletCount > 0 )
9 years ago
{
TraceResult tr;
// TankTrace needs gpGlobals->v_up, etc.
9 years ago
UTIL_MakeAimVectors( pev->angles );
9 years ago
TankTrace( barrelEnd, forward, gTankSpread[m_spread], tr );
ExplosionCreate( tr.vecEndPos, pev->angles, edict(), pev->impulse, TRUE );
CFuncTank::Fire( barrelEnd, forward, pev );
}
}
else
CFuncTank::Fire( barrelEnd, forward, pev );
}
//============================================================================
// FUNC TANK CONTROLS
//============================================================================
class CFuncTankControls : public CBaseEntity
{
public:
9 years ago
virtual int ObjectCaps( void );
9 years ago
void Spawn( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void Think( void );
9 years ago
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
9 years ago
static TYPEDESCRIPTION m_SaveData[];
CFuncTank *m_pTank;
};
LINK_ENTITY_TO_CLASS( func_tankcontrols, CFuncTankControls )
9 years ago
9 years ago
TYPEDESCRIPTION CFuncTankControls::m_SaveData[] =
9 years ago
{
DEFINE_FIELD( CFuncTankControls, m_pTank, FIELD_CLASSPTR ),
};
IMPLEMENT_SAVERESTORE( CFuncTankControls, CBaseEntity )
9 years ago
9 years ago
int CFuncTankControls::ObjectCaps( void )
9 years ago
{
9 years ago
return ( CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION ) | FCAP_IMPULSE_USE;
9 years ago
}
9 years ago
void CFuncTankControls::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
// pass the Use command onto the controls
9 years ago
if( m_pTank )
9 years ago
m_pTank->Use( pActivator, pCaller, useType, value );
ASSERT( m_pTank != NULL ); // if this fails, most likely means save/restore hasn't worked properly
}
9 years ago
void CFuncTankControls::Think( void )
9 years ago
{
edict_t *pTarget = NULL;
9 years ago
do
9 years ago
{
9 years ago
pTarget = FIND_ENTITY_BY_TARGETNAME( pTarget, STRING( pev->target ) );
} while( !FNullEnt( pTarget ) && strncmp( STRING( pTarget->v.classname ), "func_tank", 9 ) );
9 years ago
9 years ago
if( FNullEnt( pTarget ) )
9 years ago
{
9 years ago
ALERT( at_console, "No tank %s\n", STRING( pev->target ) );
9 years ago
return;
}
9 years ago
m_pTank = (CFuncTank*)Instance( pTarget );
9 years ago
}
void CFuncTankControls::Spawn( void )
{
pev->solid = SOLID_TRIGGER;
pev->movetype = MOVETYPE_NONE;
pev->effects |= EF_NODRAW;
9 years ago
SET_MODEL( ENT( pev ), STRING( pev->model ) );
9 years ago
UTIL_SetSize( pev, pev->mins, pev->maxs );
UTIL_SetOrigin( pev, pev->origin );
9 years ago
9 years ago
pev->nextthink = gpGlobals->time + 0.3; // After all the func_tank's have spawned
CBaseEntity::Spawn();
}