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.
352 lines
9.1 KiB
352 lines
9.1 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: A moving vehicle that is used as a battering ram |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "tf_vehicle_mortar.h" |
|
#include "engine/IEngineSound.h" |
|
#include "VGuiScreen.h" |
|
#include "ammodef.h" |
|
#include "in_buttons.h" |
|
#include "vehicle_mortar_shared.h" |
|
#include "movevars_shared.h" |
|
#include "mortar_round.h" |
|
|
|
|
|
// Waits this long after each shot before they can fire. |
|
#define MORTAR_FIRE_DELAY 2 |
|
|
|
|
|
#define MORTAR_MINS Vector(-30, -50, -10) |
|
#define MORTAR_MAXS Vector( 30, 50, 55) |
|
#define MORTAR_MODEL "models/objects/vehicle_mortar.mdl" |
|
#define MORTAR_SCREEN_NAME "screen_vehicle_mortar" |
|
|
|
#define ELEVATION_INTERVAL 0.3 |
|
|
|
const char *g_pMortarThinkContextName = "MortarThinkContext"; |
|
const char *g_pMortarNextFireContextName = "MortarNextFireContext"; |
|
|
|
|
|
|
|
IMPLEMENT_SERVERCLASS_ST(CVehicleMortar, DT_VehicleMortar) |
|
SendPropFloat( SENDINFO( m_flMortarYaw ), 0, SPROP_NOSCALE ), |
|
SendPropFloat( SENDINFO( m_flMortarPitch ), 0, SPROP_NOSCALE ), |
|
SendPropBool( SENDINFO( m_bAllowedToFire ) ) |
|
END_SEND_TABLE(); |
|
|
|
|
|
LINK_ENTITY_TO_CLASS(vehicle_mortar, CVehicleMortar); |
|
PRECACHE_REGISTER(vehicle_mortar); |
|
|
|
|
|
// CVars |
|
ConVar vehicle_mortar_health( "vehicle_mortar_health","800", FCVAR_NONE, "Mortar vehicle health" ); |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CVehicleMortar::CVehicleMortar() |
|
{ |
|
m_flMortarYaw = 0; |
|
m_flMortarPitch = 0; |
|
m_bAllowedToFire = true; |
|
|
|
UseClientSideAnimation(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CVehicleMortar::Precache() |
|
{ |
|
PrecacheModel( MORTAR_MODEL ); |
|
|
|
PrecacheVGuiScreen( MORTAR_SCREEN_NAME ); |
|
|
|
PrecacheScriptSound( "VehicleMortar.FireSound" ); |
|
|
|
BaseClass::Precache(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CVehicleMortar::Spawn() |
|
{ |
|
SetModel( MORTAR_MODEL ); |
|
|
|
// This size is used for placement only... |
|
SetSize(MORTAR_MINS, MORTAR_MAXS); |
|
m_takedamage = DAMAGE_YES; |
|
m_iHealth = vehicle_mortar_health.GetInt(); |
|
|
|
SetType( OBJ_VEHICLE_MORTAR ); |
|
SetMaxPassengerCount( 1 ); |
|
|
|
BaseClass::Spawn(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Gets info about the control panels |
|
//----------------------------------------------------------------------------- |
|
void CVehicleMortar::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName ) |
|
{ |
|
pPanelName = MORTAR_SCREEN_NAME; |
|
} |
|
|
|
|
|
bool CVehicleMortar::CanGetInVehicle( CBaseTFPlayer *pPlayer ) |
|
{ |
|
return ( !InDeployMode() && BaseClass::CanGetInVehicle( pPlayer ) ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Here's where we deal with weapons |
|
//----------------------------------------------------------------------------- |
|
void CVehicleMortar::OnItemPostFrame( CBaseTFPlayer *pDriver ) |
|
{ |
|
// I can't do anything if I'm not active |
|
if ( !ShouldBeActive() ) |
|
return; |
|
|
|
if ( GetPassengerRole( pDriver ) != VEHICLE_ROLE_DRIVER ) |
|
return; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CVehicleMortar::OnFinishedDeploy( void ) |
|
{ |
|
BaseClass::OnFinishedDeploy(); |
|
|
|
EntityMessageBegin( this, true ); |
|
WRITE_STRING( "OnDeployed" ); |
|
MessageEnd(); |
|
|
|
m_flMortarYaw = 0; |
|
m_flMortarPitch = 45; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CVehicleMortar::OnFinishedUnDeploy( void ) |
|
{ |
|
BaseClass::OnFinishedUnDeploy(); |
|
|
|
// Called when we are deployed. |
|
EntityMessageBegin( this, true ); |
|
WRITE_STRING( "OnUndeployed" ); |
|
MessageEnd(); |
|
|
|
m_flMortarYaw = 0; |
|
m_flMortarPitch = 0; |
|
} |
|
|
|
|
|
void CVehicleMortar::SetPassenger( int nRole, CBasePlayer *pEnt ) |
|
{ |
|
if ( pEnt ) |
|
ShowVGUIScreen( 0, false ); |
|
else |
|
ShowVGUIScreen( 0, true ); |
|
|
|
BaseClass::SetPassenger( nRole, pEnt ); |
|
} |
|
|
|
void CVehicleMortar::UpdateElevation( const Vector &vecTargetVel ) |
|
{ |
|
QAngle angles; |
|
VectorAngles( vecTargetVel, angles ); |
|
m_flMortarPitch = anglemod( -angles[PITCH] ); |
|
|
|
SetBoneController( 0, m_flMortarYaw ); |
|
SetBoneController( 1, m_flMortarPitch ); |
|
} |
|
|
|
|
|
bool CVehicleMortar::ClientCommand( CBaseTFPlayer *pPlayer, const char *pCmd, ICommandArguments *pArg ) |
|
{ |
|
ResetDeteriorationTime(); |
|
|
|
if ( !Q_stricmp( pCmd, "Deploy" ) ) |
|
{ |
|
Deploy(); |
|
return true; |
|
} |
|
else if ( !Q_stricmp( pCmd, "Undeploy" ) ) |
|
{ |
|
UnDeploy(); |
|
} |
|
else if ( !Q_stricmp( pCmd, "CancelDeploy" ) ) |
|
{ |
|
CancelDeploy(); |
|
return true; |
|
} |
|
else if ( !Q_stricmp( pCmd, "FireMortar" ) ) |
|
{ |
|
if ( pArg->Argc() == 3 ) |
|
{ |
|
FireMortar( atof( pArg->Argv(1) ), atof( pArg->Argv(2) ), false, false ); |
|
} |
|
return true; |
|
} |
|
else if ( !Q_stricmp( pCmd, "MortarYaw" ) ) |
|
{ |
|
if ( pArg->Argc() == 2 ) |
|
{ |
|
m_flMortarYaw = atof( pArg->Argv(1) ); |
|
} |
|
return true; |
|
} |
|
|
|
return BaseClass::ClientCommand( pPlayer, pCmd, pArg ); |
|
} |
|
|
|
|
|
bool CVehicleMortar::CalcFireInfo( |
|
float flFiringPower, |
|
float flFiringAccuracy, |
|
bool bRangeUpgraded, |
|
bool bAccuracyUpgraded, |
|
Vector &vStartPt, |
|
Vector &vecTargetVel, |
|
float &fallTime |
|
) |
|
{ |
|
QAngle dummy; |
|
if ( !GetAttachment( "barrel", vStartPt, dummy ) ) |
|
vStartPt = WorldSpaceCenter(); |
|
|
|
// Get target distance |
|
float flDistance; |
|
if ( bRangeUpgraded ) |
|
{ |
|
flDistance = MORTAR_RANGE_MIN + (flFiringPower * (MORTAR_RANGE_MAX_UPGRADED - MORTAR_RANGE_MIN)); |
|
} |
|
else |
|
{ |
|
flDistance = MORTAR_RANGE_MIN + (flFiringPower * (MORTAR_RANGE_MAX_INITIAL - MORTAR_RANGE_MIN)); |
|
} |
|
|
|
// Factor in inaccuracy |
|
float flInaccuracy; |
|
if ( bAccuracyUpgraded ) |
|
{ |
|
flInaccuracy = MORTAR_INACCURACY_MAX_UPGRADED * (flFiringAccuracy * 4); // flFiringAccuracy is a range from -0.25 to 0.25 |
|
} |
|
else |
|
{ |
|
flInaccuracy = MORTAR_INACCURACY_MAX_INITIAL * (flFiringAccuracy * 4); // flFiringAccuracy is a range from -0.25 to 0.25 |
|
} |
|
flDistance += (flDistance * MORTAR_DIST_INACCURACY) * random->RandomFloat( -flInaccuracy, flInaccuracy ); |
|
|
|
float flAngle = GetAbsAngles()[YAW] + m_flMortarYaw; |
|
Vector forward( -sin( DEG2RAD( flAngle ) ), cos( DEG2RAD( flAngle ) ), 0 ); |
|
Vector right( forward.y, -forward.x, 0 ); |
|
|
|
Vector vecTargetOrg = vStartPt + (forward * flDistance); |
|
// Add in sideways inaccuracy |
|
vecTargetOrg += (right * (flDistance * random->RandomFloat( -flInaccuracy, flInaccuracy )) ); |
|
|
|
// Trace down from the sky and find the point we're actually going to hit |
|
trace_t tr; |
|
Vector vecSky = vecTargetOrg + Vector(0,0,1024); |
|
UTIL_TraceLine( vecSky, vecTargetOrg, MASK_ALL, this, COLLISION_GROUP_NONE, &tr ); |
|
vecTargetOrg = tr.endpos; |
|
|
|
Vector vecMidPoint = vec3_origin; |
|
// Start with a low arc, and keep aiming higher until we've got a roughly clear shot |
|
for (int i = 512; i <= 4096; i += 512) |
|
{ |
|
trace_t tr1; |
|
trace_t tr2; |
|
|
|
vecMidPoint = Vector(0,0,i) + vStartPt + (vecTargetOrg - vStartPt) * 0.5; |
|
UTIL_TraceLine(vStartPt, vecMidPoint, MASK_ALL, this, COLLISION_GROUP_NONE, &tr1); |
|
UTIL_TraceLine(vecMidPoint, vecTargetOrg, MASK_ALL, this, COLLISION_GROUP_NONE, &tr2); |
|
|
|
// Clear shot? |
|
// We want a clear shot for the first half, and a fairly clear shot on the fall |
|
if ( tr1.fraction == 1 && tr2.fraction > 0.5 ) |
|
break; |
|
} |
|
|
|
// How high should we travel to reach the apex |
|
float distance1 = (vecMidPoint.z - vStartPt.z); |
|
float distance2 = (vecMidPoint.z - vecTargetOrg.z); |
|
|
|
// How long will it take to travel this distance |
|
float flGravity = GetCurrentGravity(); |
|
float time1 = sqrt( distance1 / (0.5 * flGravity) ); |
|
float time2 = sqrt( distance2 / (0.5 * flGravity) ); |
|
if (time1 < 0.1) |
|
return false; |
|
|
|
// how hard to launch to get there in time. |
|
vecTargetVel = (vecTargetOrg - vStartPt) / (time1 + time2); |
|
vecTargetVel.z = flGravity * time1; |
|
|
|
fallTime = time1 * 0.5; |
|
return true; |
|
} |
|
|
|
|
|
void CVehicleMortar::NextFireThink() |
|
{ |
|
// Ok, we can fire again now. |
|
m_bAllowedToFire = true; |
|
} |
|
|
|
|
|
bool CVehicleMortar::FireMortar( float flFiringPower, float flFiringAccuracy, bool bRangeUpgraded, bool bAccuracyUpgraded ) |
|
{ |
|
SetActivity( ACT_RANGE_ATTACK1 ); |
|
|
|
// Calculate the shot. |
|
Vector vStartPt; |
|
Vector vecTargetVel; |
|
float fallTime; |
|
|
|
if ( !CalcFireInfo( |
|
flFiringPower, |
|
flFiringAccuracy, |
|
bRangeUpgraded, |
|
bAccuracyUpgraded, |
|
vStartPt, |
|
vecTargetVel, |
|
fallTime ) ) |
|
{ |
|
return false; |
|
} |
|
|
|
UpdateElevation( vecTargetVel ); |
|
|
|
// Create the round |
|
CMortarRound *pRound = CMortarRound::Create( vStartPt, vecTargetVel, GetOwner()->edict() ); |
|
pRound->SetLauncher( this ); |
|
pRound->ChangeTeam( GetTeamNumber() ); |
|
pRound->SetFallTime( fallTime ); // Start a falling sound just a bit before we begin to fall |
|
pRound->SetRoundType( MA_SHELL ); |
|
|
|
// BOOM! |
|
EmitSound( "VehicleMortar.FireSound" ); |
|
|
|
// Put in a delay before thinking again. |
|
m_bAllowedToFire = false; |
|
SetContextThink( NextFireThink, gpGlobals->curtime + MORTAR_FIRE_DELAY, g_pMortarNextFireContextName ); |
|
|
|
return true; |
|
} |
|
|
|
|
|
|