diff options
Diffstat (limited to 'game/server/tf2/tf_vehicle_mortar.cpp')
| -rw-r--r-- | game/server/tf2/tf_vehicle_mortar.cpp | 352 |
1 files changed, 352 insertions, 0 deletions
diff --git a/game/server/tf2/tf_vehicle_mortar.cpp b/game/server/tf2/tf_vehicle_mortar.cpp new file mode 100644 index 0000000..d365e5c --- /dev/null +++ b/game/server/tf2/tf_vehicle_mortar.cpp @@ -0,0 +1,352 @@ +//========= 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; +} + + |