aboutsummaryrefslogtreecommitdiff
path: root/sp/src/game/server/hl2/vehicle_crane.cpp
diff options
context:
space:
mode:
authorJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
committerJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
commit39ed87570bdb2f86969d4be821c94b722dc71179 (patch)
treeabc53757f75f40c80278e87650ea92808274aa59 /sp/src/game/server/hl2/vehicle_crane.cpp
downloadsource-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz
source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip
First version of the SOurce SDK 2013
Diffstat (limited to 'sp/src/game/server/hl2/vehicle_crane.cpp')
-rw-r--r--sp/src/game/server/hl2/vehicle_crane.cpp1117
1 files changed, 1117 insertions, 0 deletions
diff --git a/sp/src/game/server/hl2/vehicle_crane.cpp b/sp/src/game/server/hl2/vehicle_crane.cpp
new file mode 100644
index 00000000..222e1864
--- /dev/null
+++ b/sp/src/game/server/hl2/vehicle_crane.cpp
@@ -0,0 +1,1117 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "vehicle_base.h"
+#include "engine/IEngineSound.h"
+#include "in_buttons.h"
+#include "soundenvelope.h"
+#include "soundent.h"
+#include "physics_saverestore.h"
+#include "vphysics/constraints.h"
+#include "vcollide_parse.h"
+#include "ndebugoverlay.h"
+#include "npc_vehicledriver.h"
+#include "vehicle_crane.h"
+#include "hl2_player.h"
+#include "rumble_shared.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define VEHICLE_HITBOX_DRIVER 1
+
+extern ConVar g_debug_vehicledriver;
+
+// Crane spring constants
+#define CRANE_SPRING_CONSTANT_HANGING 2e5f
+#define CRANE_SPRING_CONSTANT_INITIAL_RAISING (CRANE_SPRING_CONSTANT_HANGING * 0.5)
+#define CRANE_SPRING_CONSTANT_LOWERING 30.0f
+#define CRANE_SPRING_DAMPING 2e5f
+#define CRANE_SPRING_RELATIVE_DAMPING 2
+
+// Crane bones that have physics followers
+static const char *pCraneFollowerBoneNames[] =
+{
+ "base",
+ "arm",
+ "platform",
+};
+
+// Crane tip
+LINK_ENTITY_TO_CLASS( crane_tip, CCraneTip );
+
+BEGIN_DATADESC( CCraneTip )
+
+ DEFINE_PHYSPTR( m_pSpring ),
+
+END_DATADESC()
+
+// Crane
+LINK_ENTITY_TO_CLASS( prop_vehicle_crane, CPropCrane );
+
+BEGIN_DATADESC( CPropCrane )
+
+ // Inputs
+ DEFINE_INPUTFUNC( FIELD_VOID, "Lock", InputLock ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Unlock", InputUnlock ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "ForcePlayerIn", InputForcePlayerIn ),
+
+ // Keys
+ DEFINE_EMBEDDED( m_ServerVehicle ),
+ DEFINE_EMBEDDED( m_BoneFollowerManager ),
+
+ DEFINE_FIELD( m_hPlayer, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_bMagnetOn, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_hNPCDriver, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_nNPCButtons, FIELD_INTEGER ),
+ DEFINE_FIELD( m_bLocked, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bEnterAnimOn, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bExitAnimOn, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_vecEyeExitEndpoint, FIELD_POSITION_VECTOR ),
+ DEFINE_OUTPUT( m_playerOn, "PlayerOn" ),
+ DEFINE_OUTPUT( m_playerOff, "PlayerOff" ),
+ DEFINE_FIELD( m_iTurning, FIELD_INTEGER ),
+ DEFINE_FIELD( m_bStartSoundAtCrossover, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_flTurn, FIELD_FLOAT ),
+ DEFINE_FIELD( m_bExtending, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_flExtension, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flExtensionRate, FIELD_FLOAT ),
+ DEFINE_FIELD( m_bDropping, FIELD_BOOLEAN ),
+ //DEFINE_FIELD( m_flNextDangerSoundTime, FIELD_TIME ),
+ //DEFINE_FIELD( m_flNextCreakSound, FIELD_TIME ),
+ DEFINE_FIELD( m_flNextDropAllowedTime, FIELD_TIME ),
+ DEFINE_FIELD( m_flSlowRaiseTime, FIELD_TIME ),
+ DEFINE_FIELD( m_flMaxExtensionSpeed, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flMaxTurnSpeed, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flExtensionAccel, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flExtensionDecel, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flTurnAccel, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flTurnDecel, FIELD_FLOAT ),
+ DEFINE_KEYFIELD( m_iszMagnetName, FIELD_STRING, "magnetname" ),
+ DEFINE_FIELD( m_hCraneMagnet, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hCraneTip, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hRope, FIELD_EHANDLE ),
+ DEFINE_PHYSPTR( m_pConstraintGroup ),
+ DEFINE_KEYFIELD( m_vehicleScript, FIELD_STRING, "vehiclescript" ),
+
+END_DATADESC()
+
+IMPLEMENT_SERVERCLASS_ST(CPropCrane, DT_PropCrane)
+ SendPropEHandle(SENDINFO(m_hPlayer)),
+ SendPropBool(SENDINFO(m_bMagnetOn)),
+ SendPropBool(SENDINFO(m_bEnterAnimOn)),
+ SendPropBool(SENDINFO(m_bExitAnimOn)),
+ SendPropVector(SENDINFO(m_vecEyeExitEndpoint), -1, SPROP_COORD),
+END_SEND_TABLE();
+
+
+//------------------------------------------------
+// Precache
+//------------------------------------------------
+void CPropCrane::Precache( void )
+{
+ BaseClass::Precache();
+ m_ServerVehicle.Initialize( STRING(m_vehicleScript) );
+}
+
+
+//------------------------------------------------
+// Spawn
+//------------------------------------------------
+void CPropCrane::Spawn( void )
+{
+ Precache();
+ SetModel( STRING( GetModelName() ) );
+ SetCollisionGroup( COLLISION_GROUP_VEHICLE );
+
+ BaseClass::Spawn();
+
+ SetSolid( SOLID_BBOX );
+ AddSolidFlags( FSOLID_NOT_SOLID );
+ SetMoveType( MOVETYPE_NOCLIP );
+
+ m_takedamage = DAMAGE_EVENTS_ONLY;
+ m_flTurn = 0;
+ m_flExtension = 0;
+ m_flNextDangerSoundTime = 0;
+ m_flNextCreakSound = 0;
+ m_flNextDropAllowedTime = 0;
+ m_flSlowRaiseTime = 0;
+ m_bDropping = false;
+ m_bMagnetOn = false;
+
+ InitCraneSpeeds();
+
+ SetPoseParameter( "armextensionpose", m_flExtension );
+
+ CreateVPhysics();
+ SetNextThink( gpGlobals->curtime );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropCrane::Activate( void )
+{
+ BaseClass::Activate();
+
+ // If we load a game, we don't need to set this all up again.
+ if ( m_hCraneMagnet )
+ return;
+
+ // Find our magnet
+ if ( m_iszMagnetName == NULL_STRING )
+ {
+ Warning( "prop_vehicle_crane %s has no magnet entity specified!\n", STRING(GetEntityName()) );
+ UTIL_Remove( this );
+ return;
+ }
+
+ m_hCraneMagnet = dynamic_cast<CPhysMagnet *>(gEntList.FindEntityByName( NULL, STRING(m_iszMagnetName) ));
+ if ( !m_hCraneMagnet )
+ {
+ Warning( "prop_vehicle_crane %s failed to find magnet %s.\n", STRING(GetEntityName()), STRING(m_iszMagnetName) );
+ UTIL_Remove( this );
+ return;
+ }
+
+ // We want the magnet to cast a long shadow
+ m_hCraneMagnet->SetShadowCastDistance( 2048 );
+
+ // Create our constraint group
+ constraint_groupparams_t group;
+ group.Defaults();
+ m_pConstraintGroup = physenv->CreateConstraintGroup( group );
+ m_hCraneMagnet->SetConstraintGroup( m_pConstraintGroup );
+
+ // Create our crane tip
+ Vector vecOrigin;
+ QAngle vecAngles;
+ GetCraneTipPosition( &vecOrigin, &vecAngles );
+ m_hCraneTip = CCraneTip::Create( m_hCraneMagnet, m_pConstraintGroup, vecOrigin, vecAngles );
+ if ( !m_hCraneTip )
+ {
+ UTIL_Remove( this );
+ return;
+ }
+ m_pConstraintGroup->Activate();
+
+ // Make a rope to connect 'em
+ int iIndex = m_hCraneMagnet->LookupAttachment("magnetcable_a");
+ m_hRope = CRopeKeyframe::Create( this, m_hCraneMagnet, 1, iIndex );
+ if ( m_hRope )
+ {
+ m_hRope->m_Width = 3;
+ m_hRope->m_nSegments = ROPE_MAX_SEGMENTS / 2;
+ m_hRope->EnableWind( false );
+ m_hRope->SetupHangDistance( 0 );
+ m_hRope->m_RopeLength = (m_hCraneMagnet->GetAbsOrigin() - m_hCraneTip->GetAbsOrigin()).Length() * 1.1;
+ }
+
+ // Start with the magnet off
+ TurnMagnetOff();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CPropCrane::CreateVPhysics( void )
+{
+ BaseClass::CreateVPhysics();
+ m_BoneFollowerManager.InitBoneFollowers( this, ARRAYSIZE(pCraneFollowerBoneNames), pCraneFollowerBoneNames );
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropCrane::UpdateOnRemove( void )
+{
+ m_BoneFollowerManager.DestroyBoneFollowers();
+ BaseClass::UpdateOnRemove();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropCrane::InitCraneSpeeds( void )
+{
+ m_flMaxExtensionSpeed = CRANE_EXTENSION_RATE_MAX * 2;
+ m_flMaxTurnSpeed = CRANE_TURN_RATE_MAX * 2;
+ m_flExtensionAccel = CRANE_EXTENSION_ACCEL * 2;
+ m_flExtensionDecel = CRANE_EXTENSION_DECEL * 2;
+ m_flTurnAccel = CRANE_TURN_ACCEL * 2;
+ m_flTurnDecel = CRANE_DECEL * 2;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropCrane::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
+{
+ if ( ptr->hitbox == VEHICLE_HITBOX_DRIVER )
+ {
+ if ( m_hPlayer != NULL )
+ {
+ m_hPlayer->TakeDamage( info );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CPropCrane::OnTakeDamage( const CTakeDamageInfo &inputInfo )
+{
+ //Do scaled up physics damage to the car
+ CTakeDamageInfo info = inputInfo;
+ info.ScaleDamage( 25 );
+
+ // reset the damage
+ info.SetDamage( inputInfo.GetDamage() );
+
+ //Check to do damage to driver
+ if ( m_hPlayer != NULL )
+ {
+ //Take no damage from physics damages
+ if ( info.GetDamageType() & DMG_CRUSH )
+ return 0;
+
+ //Take the damage
+ m_hPlayer->TakeDamage( info );
+ }
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Vector CPropCrane::BodyTarget( const Vector &posSrc, bool bNoisy )
+{
+ Vector shotPos;
+ matrix3x4_t matrix;
+
+ int eyeAttachmentIndex = LookupAttachment("vehicle_driver_eyes");
+ GetAttachment( eyeAttachmentIndex, matrix );
+ MatrixGetColumn( matrix, 3, shotPos );
+
+ if ( bNoisy )
+ {
+ shotPos[0] += random->RandomFloat( -8.0f, 8.0f );
+ shotPos[1] += random->RandomFloat( -8.0f, 8.0f );
+ shotPos[2] += random->RandomFloat( -8.0f, 8.0f );
+ }
+
+ return shotPos;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropCrane::Think(void)
+{
+ SetNextThink( gpGlobals->curtime + 0.1 );
+
+ if ( GetDriver() )
+ {
+ BaseClass::Think();
+
+ if ( m_hNPCDriver )
+ {
+ GetServerVehicle()->NPC_DriveVehicle();
+ }
+
+ // play enter animation
+ StudioFrameAdvance();
+
+ // If the enter or exit animation has finished, tell the server vehicle
+ if ( IsSequenceFinished() && (m_bExitAnimOn || m_bEnterAnimOn) )
+ {
+ if ( m_bEnterAnimOn )
+ {
+ // Finished entering, display the hint for using the crane
+ UTIL_HudHintText( m_hPlayer, "#Valve_Hint_CraneKeys" );
+ }
+
+ GetServerVehicle()->HandleEntryExitFinish( m_bExitAnimOn, true );
+ }
+ }
+ else
+ {
+ // Run the crane's movement
+ RunCraneMovement( 0.1 );
+ }
+
+ // Update follower bones
+ m_BoneFollowerManager.UpdateBoneFollowers(this);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *player -
+//-----------------------------------------------------------------------------
+void CPropCrane::ItemPostFrame( CBasePlayer *player )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropCrane::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
+{
+ CBasePlayer *pPlayer = ToBasePlayer( pActivator );
+ if ( !pPlayer )
+ return;
+
+ ResetUseKey( pPlayer );
+
+ GetServerVehicle()->HandlePassengerEntry( pPlayer, (value>0) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true of the player's allowed to enter / exit the vehicle
+//-----------------------------------------------------------------------------
+bool CPropCrane::CanEnterVehicle( CBaseEntity *pEntity )
+{
+ // Prevent entering if the vehicle's being driven by an NPC
+ if ( GetDriver() && GetDriver() != pEntity )
+ return false;
+
+ // Prevent entering if the vehicle's locked
+ return ( !m_bLocked );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true of the player's allowed to enter / exit the vehicle
+//-----------------------------------------------------------------------------
+bool CPropCrane::CanExitVehicle( CBaseEntity *pEntity )
+{
+ // Prevent exiting if the vehicle's locked, or rotating
+ // Adrian: Check also if I'm currently jumping in or out.
+ return ( !m_bLocked && (GetLocalAngularVelocity() == vec3_angle) && m_bExitAnimOn == false && m_bEnterAnimOn == false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Override base class to add display
+//-----------------------------------------------------------------------------
+void CPropCrane::DrawDebugGeometryOverlays(void)
+{
+ // Draw if BBOX is on
+ if ( m_debugOverlays & OVERLAY_BBOX_BIT )
+ {
+ Vector vecPoint = m_hCraneMagnet->GetAbsOrigin();
+ int iIndex = m_hCraneMagnet->LookupAttachment("magnetcable_a");
+ if ( iIndex >= 0 )
+ {
+ m_hCraneMagnet->GetAttachment( iIndex, vecPoint );
+ }
+
+ NDebugOverlay::Line( m_hCraneTip->GetAbsOrigin(), vecPoint, 255,255,255, true, 0.1 );
+ }
+
+ BaseClass::DrawDebugGeometryOverlays();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropCrane::EnterVehicle( CBaseCombatCharacter *pPassenger )
+{
+ if ( pPassenger == NULL )
+ return;
+
+ CBasePlayer *pPlayer = ToBasePlayer( pPassenger );
+ if ( pPlayer != NULL )
+ {
+ // Remove any player who may be in the vehicle at the moment
+ if ( m_hPlayer )
+ {
+ ExitVehicle( VEHICLE_ROLE_DRIVER );
+ }
+
+ m_hPlayer = pPlayer;
+ m_playerOn.FireOutput( pPlayer, this, 0 );
+
+ m_hPlayer->RumbleEffect( RUMBLE_FLAT_BOTH, 0, RUMBLE_FLAG_LOOP );
+ m_hPlayer->RumbleEffect( RUMBLE_FLAT_BOTH, 10, RUMBLE_FLAG_UPDATE_SCALE );
+
+ m_ServerVehicle.SoundStart();
+ }
+ else
+ {
+ // NPCs not yet supported - jdw
+ Assert( 0 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropCrane::ExitVehicle( int nRole )
+{
+ CBasePlayer *pPlayer = m_hPlayer;
+ if ( !pPlayer )
+ return;
+
+ m_hPlayer = NULL;
+ ResetUseKey( pPlayer );
+ m_playerOff.FireOutput( pPlayer, this, 0 );
+ m_bEnterAnimOn = false;
+
+ m_ServerVehicle.SoundShutdown( 1.0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropCrane::ResetUseKey( CBasePlayer *pPlayer )
+{
+ pPlayer->m_afButtonPressed &= ~IN_USE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Pass player movement into the crane's driving system
+//-----------------------------------------------------------------------------
+void CPropCrane::SetupMove( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move )
+{
+ // If the player's entering/exiting the vehicle, prevent movement
+ if ( !m_bEnterAnimOn && !m_bExitAnimOn )
+ {
+ int buttons = ucmd->buttons;
+ if ( !(buttons & (IN_MOVELEFT|IN_MOVERIGHT)) )
+ {
+ if ( ucmd->sidemove < 0 )
+ {
+ buttons |= IN_MOVELEFT;
+ }
+ else if ( ucmd->sidemove > 0 )
+ {
+ buttons |= IN_MOVERIGHT;
+ }
+ }
+ DriveCrane( buttons, player->m_afButtonPressed );
+ }
+
+ // Run the crane's movement
+ RunCraneMovement( gpGlobals->frametime );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Crane rotates around with +left and +right, and extends/retracts
+// the cable with +forward and +back.
+//-----------------------------------------------------------------------------
+void CPropCrane::DriveCrane( int iDriverButtons, int iButtonsPressed, float flNPCSteering )
+{
+ bool bWasExtending = m_bExtending;
+
+ // Handle rotation of the crane
+ if ( iDriverButtons & IN_MOVELEFT )
+ {
+ // NPCs may cheat and set the steering
+ if ( flNPCSteering )
+ {
+ m_flTurn = flNPCSteering;
+ }
+ else
+ {
+ // Try adding some randomness to make it feel shaky?
+ float flTurnAdd = m_flTurnAccel;
+ // If we're turning back on ourselves, use decel speed
+ if ( m_flTurn < 0 )
+ {
+ flTurnAdd = MAX( flTurnAdd, m_flTurnDecel );
+ }
+
+ m_flTurn = UTIL_Approach( m_flMaxTurnSpeed, m_flTurn, flTurnAdd * gpGlobals->frametime );
+ }
+ m_iTurning = TURNING_LEFT;
+ }
+ else if ( iDriverButtons & IN_MOVERIGHT )
+ {
+ // NPCs may cheat and set the steering
+ if ( flNPCSteering )
+ {
+ m_flTurn = flNPCSteering;
+ }
+ else
+ {
+ // Try adding some randomness to make it feel shaky?
+ float flTurnAdd = m_flTurnAccel;
+ // If we're turning back on ourselves, increase the rate
+ if ( m_flTurn > 0 )
+ {
+ flTurnAdd = MAX( flTurnAdd, m_flTurnDecel );
+ }
+ m_flTurn = UTIL_Approach( -m_flMaxTurnSpeed, m_flTurn, flTurnAdd * gpGlobals->frametime );
+ }
+ m_iTurning = TURNING_RIGHT;
+ }
+ else
+ {
+ m_flTurn = UTIL_Approach( 0, m_flTurn, m_flTurnDecel * gpGlobals->frametime );
+ m_iTurning = TURNING_NOT;
+ }
+
+ if ( m_hPlayer )
+ {
+ float maxTurn = GetMaxTurnRate();
+ static float maxRumble = 0.35f;
+ static float minRumble = 0.1f;
+ float rumbleRange = maxRumble - minRumble;
+ float rumble;
+
+ float factor = fabs(m_flTurn) / maxTurn;
+ factor = MIN( factor, 1.0f );
+ rumble = minRumble + (rumbleRange * factor);
+
+ m_hPlayer->RumbleEffect( RUMBLE_FLAT_BOTH, (int)(rumble * 100), RUMBLE_FLAG_UPDATE_SCALE );
+ }
+
+ SetLocalAngularVelocity( QAngle(0,m_flTurn * 10,0) );
+
+ // Handle extension / retraction of the arm
+ if ( iDriverButtons & IN_FORWARD )
+ {
+ m_flExtensionRate = UTIL_Approach( m_flMaxExtensionSpeed, m_flExtensionRate, m_flExtensionAccel * gpGlobals->frametime );
+ m_bExtending = true;
+ }
+ else if ( iDriverButtons & IN_BACK )
+ {
+ m_flExtensionRate = UTIL_Approach( -m_flMaxExtensionSpeed, m_flExtensionRate, m_flExtensionAccel * gpGlobals->frametime );
+ m_bExtending = true;
+ }
+ else
+ {
+ m_flExtensionRate = UTIL_Approach( 0, m_flExtensionRate, m_flExtensionDecel * gpGlobals->frametime );
+ m_bExtending = false;
+ }
+
+ //Msg("Turn: %f\nExtensionRate: %f\n", m_flTurn, m_flExtensionRate );
+
+ //If we're holding down an attack button, update our state
+ if ( iButtonsPressed & (IN_ATTACK | IN_ATTACK2) )
+ {
+ // If we have something on the magnet, turn the magnet off
+ if ( m_hCraneMagnet->GetTotalMassAttachedObjects() )
+ {
+ TurnMagnetOff();
+ }
+ else if ( !m_bDropping && m_flNextDropAllowedTime < gpGlobals->curtime )
+ {
+ TurnMagnetOn();
+
+ // Drop the magnet till it hits something
+ m_bDropping = true;
+ m_hCraneMagnet->ResetHasHitSomething();
+ m_hCraneTip->m_pSpring->SetSpringConstant( CRANE_SPRING_CONSTANT_LOWERING );
+
+ m_ServerVehicle.PlaySound( VS_MISC1 );
+ }
+ }
+
+ float flSpeedPercentage = clamp( fabs(m_flTurn) / m_flMaxTurnSpeed, 0, 1 );
+ vbs_sound_update_t params;
+ params.Defaults();
+ params.bThrottleDown = (m_iTurning != TURNING_NOT);
+ params.flCurrentSpeedFraction = flSpeedPercentage;
+ params.flWorldSpaceSpeed = 0;
+
+ m_ServerVehicle.SoundUpdate( params );
+
+ // Play sounds for arm extension / retraction
+ if ( m_bExtending && !bWasExtending )
+ {
+ m_ServerVehicle.StopSound( VS_ENGINE2_STOP );
+ m_ServerVehicle.PlaySound( VS_ENGINE2_START );
+ }
+ else if ( !m_bExtending && bWasExtending )
+ {
+ m_ServerVehicle.StopSound( VS_ENGINE2_START );
+ m_ServerVehicle.PlaySound( VS_ENGINE2_STOP );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropCrane::RecalculateCraneTip( void )
+{
+ Vector vecOrigin;
+ QAngle vecAngles;
+ GetCraneTipPosition( &vecOrigin, &vecAngles );
+ m_hCraneTip->SetAbsOrigin( vecOrigin );
+
+ // NOTE: We need to do this because we're not using Physics...
+ if ( m_hCraneTip->VPhysicsGetObject() )
+ {
+ m_hCraneTip->VPhysicsGetObject()->UpdateShadow( vecOrigin, vec3_angle, true, TICK_INTERVAL * 2.0f );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pPlayer -
+// *pMoveData -
+//-----------------------------------------------------------------------------
+void CPropCrane::RunCraneMovement( float flTime )
+{
+ if ( m_flExtensionRate )
+ {
+ // Extend / Retract the crane
+ m_flExtension = clamp( m_flExtension + (m_flExtensionRate * 10 * flTime), 0, 2 );
+ SetPoseParameter( "armextensionpose", m_flExtension );
+ StudioFrameAdvance();
+ }
+
+ // Drop the magnet until it hits the ground
+ if ( m_bDropping )
+ {
+ // Drop until the magnet hits something
+ if ( m_hCraneMagnet->HasHitSomething() )
+ {
+ // We hit the ground, stop dropping
+ m_hCraneTip->m_pSpring->SetSpringConstant( CRANE_SPRING_CONSTANT_INITIAL_RAISING );
+ m_bDropping = false;
+ m_flNextDropAllowedTime = gpGlobals->curtime + 3.0;
+ m_flSlowRaiseTime = gpGlobals->curtime;
+
+ m_ServerVehicle.PlaySound( VS_MISC2 );
+ }
+ }
+ else if ( (m_flSlowRaiseTime + CRANE_SLOWRAISE_TIME) > gpGlobals->curtime )
+ {
+ float flDelta = (gpGlobals->curtime - m_flSlowRaiseTime);
+
+ flDelta = clamp( flDelta, 0, CRANE_SLOWRAISE_TIME );
+ float flCurrentSpringConstant = RemapVal( flDelta, 0, CRANE_SLOWRAISE_TIME, CRANE_SPRING_CONSTANT_INITIAL_RAISING, CRANE_SPRING_CONSTANT_HANGING );
+ m_hCraneTip->m_pSpring->SetSpringConstant( flCurrentSpringConstant );
+ }
+
+ // If we've moved in any way, update the tip
+ if ( m_bDropping || m_flExtensionRate || GetLocalAngularVelocity() != vec3_angle )
+ {
+ RecalculateCraneTip();
+ }
+
+ // Make danger sounds underneath the magnet if we have something attached to it
+ /*
+ if ( (m_flNextDangerSoundTime < gpGlobals->curtime) && (m_hCraneMagnet->GetTotalMassAttachedObjects() > 0) )
+ {
+ // Trace down from the magnet and make a danger sound on the ground
+ trace_t tr;
+ Vector vecSource = m_hCraneMagnet->GetAbsOrigin();
+ UTIL_TraceLine( vecSource, vecSource - Vector(0,0,2048), MASK_SOLID_BRUSHONLY, m_hCraneMagnet, 0, &tr );
+
+ if ( tr.fraction < 1.0 )
+ {
+ // Make the volume proportional to the amount of mass on the magnet
+ float flVolume = clamp( (m_hCraneMagnet->GetTotalMassAttachedObjects() * 0.5), 100.f, 600.f );
+ CSoundEnt::InsertSound( SOUND_DANGER, tr.endpos, flVolume, 0.2, this );
+
+ //Msg("Total: %.2f Volume: %.2f\n", m_hCraneMagnet->GetTotalMassAttachedObjects(), flVolume );
+ //Vector vecVolume = Vector(flVolume,flVolume,flVolume) * 0.5;
+ //NDebugOverlay::Box( tr.endpos, -vecVolume, vecVolume, 255,0,0, false, 0.3 );
+ //NDebugOverlay::Cross3D( tr.endpos, -Vector(10,10,10), Vector(10,10,10), 255,0,0, false, 0.3 );
+ }
+
+ m_flNextDangerSoundTime = gpGlobals->curtime + 0.3;
+ }
+ */
+
+ // Play creak sounds on the magnet if there's heavy weight on it
+ if ( (m_flNextCreakSound < gpGlobals->curtime) && (m_hCraneMagnet->GetTotalMassAttachedObjects() > 100) )
+ {
+ // Randomly play creaks from the magnet, and increase the chance based on the turning speed
+ float flSpeedPercentage = clamp( fabs(m_flTurn) / m_flMaxTurnSpeed, 0, 1 );
+ if ( RandomFloat(0,1) > (0.95 - (0.1 * flSpeedPercentage)) )
+ {
+ if ( m_ServerVehicle.m_vehicleSounds.iszSound[VS_MISC4] != NULL_STRING )
+ {
+ CPASAttenuationFilter filter( m_hCraneMagnet );
+
+ EmitSound_t ep;
+ ep.m_nChannel = CHAN_VOICE;
+ ep.m_pSoundName = STRING(m_ServerVehicle.m_vehicleSounds.iszSound[VS_MISC4]);
+ ep.m_flVolume = 1.0f;
+ ep.m_SoundLevel = SNDLVL_NORM;
+
+ CBaseEntity::EmitSound( filter, m_hCraneMagnet->entindex(), ep );
+ }
+ m_flNextCreakSound = gpGlobals->curtime + 5.0;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropCrane::TurnMagnetOn( void )
+{
+ if ( !m_hCraneMagnet->IsOn() )
+ {
+ variant_t emptyVariant;
+ m_hCraneMagnet->AcceptInput( "Toggle", this, this, emptyVariant, USE_TOGGLE );
+ m_ServerVehicle.PlaySound( VS_MISC3 );
+
+ m_bMagnetOn = true;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropCrane::TurnMagnetOff( void )
+{
+ if ( m_hCraneMagnet->IsOn() )
+ {
+ variant_t emptyVariant;
+ m_hCraneMagnet->AcceptInput( "Toggle", this, this, emptyVariant, USE_TOGGLE );
+ m_ServerVehicle.PlaySound( VS_MISC3 );
+
+ m_bMagnetOn = false;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CPropCrane::GetCraneTipPosition( void )
+{
+ return m_hCraneTip->GetAbsOrigin();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Fills out the values with the desired position of the crane's tip
+//-----------------------------------------------------------------------------
+void CPropCrane::GetCraneTipPosition( Vector *vecOrigin, QAngle *vecAngles )
+{
+ GetAttachment( "cable_tip", *vecOrigin, *vecAngles );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Vehicles are permanently oriented off angle for vphysics.
+//-----------------------------------------------------------------------------
+void CPropCrane::GetVectors(Vector* pForward, Vector* pRight, Vector* pUp) const
+{
+ // This call is necessary to cause m_rgflCoordinateFrame to be recomputed
+ const matrix3x4_t &entityToWorld = EntityToWorldTransform();
+
+ if (pForward != NULL)
+ {
+ MatrixGetColumn( entityToWorld, 1, *pForward );
+ }
+
+ if (pRight != NULL)
+ {
+ MatrixGetColumn( entityToWorld, 0, *pRight );
+ }
+
+ if (pUp != NULL)
+ {
+ MatrixGetColumn( entityToWorld, 2, *pUp );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBaseEntity *CPropCrane::GetDriver( void )
+{
+ if ( m_hNPCDriver )
+ return m_hNPCDriver;
+
+ return m_hPlayer;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Prevent the player from entering / exiting the vehicle
+//-----------------------------------------------------------------------------
+void CPropCrane::InputLock( inputdata_t &inputdata )
+{
+ m_bLocked = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Allow the player to enter / exit the vehicle
+//-----------------------------------------------------------------------------
+void CPropCrane::InputUnlock( inputdata_t &inputdata )
+{
+ m_bLocked = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &inputdata -
+//-----------------------------------------------------------------------------
+void CPropCrane::InputForcePlayerIn( inputdata_t &inputdata )
+{
+ CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
+ if ( pPlayer && !m_hPlayer )
+ {
+ GetServerVehicle()->HandlePassengerEntry( pPlayer, 0 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropCrane::SetNPCDriver( CNPC_VehicleDriver *pDriver )
+{
+ m_hNPCDriver = pDriver;
+ m_nNPCButtons = 0;
+
+ if ( pDriver )
+ {
+ m_flMaxExtensionSpeed = CRANE_EXTENSION_RATE_MAX * 1.5;
+ m_flMaxTurnSpeed = CRANE_TURN_RATE_MAX * 1.5;
+ m_flExtensionAccel = CRANE_EXTENSION_ACCEL * 2;
+ m_flExtensionDecel = CRANE_EXTENSION_DECEL * 20; // Npcs stop quickly to make them more accurate
+ m_flTurnAccel = CRANE_TURN_ACCEL * 2;
+ m_flTurnDecel = CRANE_DECEL * 10; // Npcs stop quickly to make them more accurate
+
+ // Set our owner entity to be the NPC, so it can path check without hitting us
+ SetOwnerEntity( pDriver );
+ }
+ else
+ {
+ // Restore player crane speeds
+ InitCraneSpeeds();
+ SetOwnerEntity( NULL );
+
+ // Shutdown the crane's sounds
+ m_ServerVehicle.SoundShutdown( 1.0 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Allows us to turn off the rumble
+//-----------------------------------------------------------------------------
+void CPropCrane::PreExitVehicle( CBaseCombatCharacter *pPlayer, int nRole )
+{
+ if ( pPlayer != m_hPlayer )
+ return;
+
+ if ( m_hPlayer != NULL )
+ {
+ // Stop rumbles
+ m_hPlayer->RumbleEffect( RUMBLE_FLAT_BOTH, 0, RUMBLE_FLAG_STOP );
+ }
+}
+
+//========================================================================================================================================
+// CRANE VEHICLE SERVER VEHICLE
+//========================================================================================================================================
+CPropCrane *CCraneServerVehicle::GetCrane( void )
+{
+ return (CPropCrane*)GetDrivableVehicle();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCraneServerVehicle::GetVehicleViewPosition( int nRole, Vector *pAbsOrigin, QAngle *pAbsAngles, float *pFOV /*= NULL*/ )
+{
+ // FIXME: This needs to be reconciled with the other versions of this function!
+ Assert( nRole == VEHICLE_ROLE_DRIVER );
+ CBasePlayer *pPlayer = ToBasePlayer( GetDrivableVehicle()->GetDriver() );
+ Assert( pPlayer );
+
+ *pAbsAngles = pPlayer->EyeAngles(); // yuck. this is an in/out parameter.
+
+ float flPitchFactor = 1.0;
+ matrix3x4_t vehicleEyePosToWorld;
+ Vector vehicleEyeOrigin;
+ QAngle vehicleEyeAngles;
+ GetCrane()->GetAttachment( "vehicle_driver_eyes", vehicleEyeOrigin, vehicleEyeAngles );
+ AngleMatrix( vehicleEyeAngles, vehicleEyePosToWorld );
+
+ // Compute the relative rotation between the unperterbed eye attachment + the eye angles
+ matrix3x4_t cameraToWorld;
+ AngleMatrix( *pAbsAngles, cameraToWorld );
+
+ matrix3x4_t worldToEyePos;
+ MatrixInvert( vehicleEyePosToWorld, worldToEyePos );
+
+ matrix3x4_t vehicleCameraToEyePos;
+ ConcatTransforms( worldToEyePos, cameraToWorld, vehicleCameraToEyePos );
+
+ // Now perterb the attachment point
+ vehicleEyeAngles.x = RemapAngleRange( PITCH_CURVE_ZERO * flPitchFactor, PITCH_CURVE_LINEAR, vehicleEyeAngles.x );
+ vehicleEyeAngles.z = RemapAngleRange( ROLL_CURVE_ZERO * flPitchFactor, ROLL_CURVE_LINEAR, vehicleEyeAngles.z );
+ AngleMatrix( vehicleEyeAngles, vehicleEyeOrigin, vehicleEyePosToWorld );
+
+ // Now treat the relative eye angles as being relative to this new, perterbed view position...
+ matrix3x4_t newCameraToWorld;
+ ConcatTransforms( vehicleEyePosToWorld, vehicleCameraToEyePos, newCameraToWorld );
+
+ // output new view abs angles
+ MatrixAngles( newCameraToWorld, *pAbsAngles );
+
+ // UNDONE: *pOrigin would already be correct in single player if the HandleView() on the server ran after vphysics
+ MatrixGetColumn( newCameraToWorld, 3, *pAbsOrigin );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCraneServerVehicle::NPC_SetDriver( CNPC_VehicleDriver *pDriver )
+{
+ GetCrane()->SetNPCDriver( pDriver );
+
+ if ( pDriver )
+ {
+ SetVehicleVolume( 1.0 ); // Vehicles driven by NPCs are louder
+ GetCrane()->SetSimulatedEveryTick( false );
+ }
+ else
+ {
+ SetVehicleVolume( 0.5 );
+ GetCrane()->SetSimulatedEveryTick( true );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCraneServerVehicle::NPC_DriveVehicle( void )
+{
+ if ( g_debug_vehicledriver.GetInt() )
+ {
+ if ( m_nNPCButtons )
+ {
+ Vector vecForward, vecRight;
+ GetCrane()->GetVectors( &vecForward, &vecRight, NULL );
+ if ( m_nNPCButtons & IN_FORWARD )
+ {
+ NDebugOverlay::Line( GetCrane()->GetAbsOrigin(), GetCrane()->GetAbsOrigin() + vecForward * 200, 0,255,0, true, 0.1 );
+ }
+ if ( m_nNPCButtons & IN_BACK )
+ {
+ NDebugOverlay::Line( GetCrane()->GetAbsOrigin(), GetCrane()->GetAbsOrigin() - vecForward * 200, 0,255,0, true, 0.1 );
+ }
+ if ( m_nNPCButtons & IN_MOVELEFT )
+ {
+ NDebugOverlay::Line( GetCrane()->GetAbsOrigin(), GetCrane()->GetAbsOrigin() - vecRight * 200, 0,255,0, true, 0.1 );
+ }
+ if ( m_nNPCButtons & IN_MOVERIGHT )
+ {
+ NDebugOverlay::Line( GetCrane()->GetAbsOrigin(), GetCrane()->GetAbsOrigin() + vecRight * 200, 0,255,0, true, 0.1 );
+ }
+ if ( m_nNPCButtons & IN_JUMP )
+ {
+ NDebugOverlay::Box( GetCrane()->GetAbsOrigin(), -Vector(20,20,20), Vector(20,20,20), 0,255,0, true, 0.1 );
+ }
+ }
+ }
+
+ GetCrane()->DriveCrane( m_nNPCButtons, m_nNPCButtons, m_flTurnDegrees );
+
+ // Clear out attack buttons each frame
+ m_nNPCButtons &= ~IN_ATTACK;
+ m_nNPCButtons &= ~IN_ATTACK2;
+
+ // Run the crane's movement
+ GetCrane()->RunCraneMovement( 0.1 );
+}
+
+//===============================================================================================================================
+// CRANE CABLE TIP
+//===============================================================================================================================
+//-----------------------------------------------------------------------------
+// Purpose: To by usable by the constraint system, this needs to have a phys model.
+//-----------------------------------------------------------------------------
+void CCraneTip::Spawn( void )
+{
+ Precache();
+ SetModel( "models/props_junk/cardboard_box001a.mdl" );
+ AddEffects( EF_NODRAW );
+
+ // We don't want this to be solid, because we don't want it to collide with the hydra.
+ SetSolid( SOLID_VPHYSICS );
+ AddSolidFlags( FSOLID_NOT_SOLID );
+ VPhysicsInitShadow( false, false );
+
+ // Disable movement on this sucker, we're going to move him manually
+ SetMoveType( MOVETYPE_NONE );
+
+ BaseClass::Spawn();
+
+ m_pSpring = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCraneTip::Precache( void )
+{
+ PrecacheModel( "models/props_junk/cardboard_box001a.mdl" );
+ BaseClass::Precache();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Activate/create the constraint
+//-----------------------------------------------------------------------------
+bool CCraneTip::CreateConstraint( CBaseAnimating *pCraneMagnet, IPhysicsConstraintGroup *pGroup )
+{
+ IPhysicsObject *pPhysObject = VPhysicsGetObject();
+ IPhysicsObject *pCraneMagnetPhysObject = pCraneMagnet->VPhysicsGetObject();
+ if ( !pCraneMagnetPhysObject )
+ {
+ Msg(" Error: Tried to create a crane_tip with a crane magnet that has no physics model.\n" );
+ return false;
+ }
+ Assert( pPhysObject );
+
+ // Check to see if it's got an attachment point to connect to
+ Vector vecPoint = pCraneMagnet->GetAbsOrigin();
+ int iIndex = pCraneMagnet->LookupAttachment("magnetcable_a");
+ if ( iIndex >= 0 )
+ {
+ pCraneMagnet->GetAttachment( iIndex, vecPoint );
+ }
+
+ // Create our spring
+ /*
+ constraint_lengthparams_t length;
+ length.Defaults();
+ length.InitWorldspace( pPhysObject, pCraneMagnetPhysObject, GetAbsOrigin(), vecPoint );
+ length.constraint.Defaults();
+ m_pConstraint = physenv->CreateLengthConstraint( pPhysObject, pCraneMagnetPhysObject, pGroup, length );
+ */
+
+ springparams_t spring;
+ spring.constant = CRANE_SPRING_CONSTANT_HANGING;
+ spring.damping = CRANE_SPRING_DAMPING;
+ spring.naturalLength = (GetAbsOrigin() - vecPoint).Length();
+ spring.relativeDamping = CRANE_SPRING_RELATIVE_DAMPING;
+ spring.startPosition = GetAbsOrigin();
+ spring.endPosition = vecPoint;
+ spring.useLocalPositions = false;
+ spring.onlyStretch = true;
+ m_pSpring = physenv->CreateSpring( pPhysObject, pCraneMagnetPhysObject, &spring );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a Hydra Impale between the hydra and the entity passed in
+//-----------------------------------------------------------------------------
+CCraneTip *CCraneTip::Create( CBaseAnimating *pCraneMagnet, IPhysicsConstraintGroup *pGroup, const Vector &vecOrigin, const QAngle &vecAngles )
+{
+ CCraneTip *pCraneTip = (CCraneTip *)CBaseEntity::Create( "crane_tip", vecOrigin, vecAngles );
+ if ( !pCraneTip )
+ return NULL;
+
+ if ( !pCraneTip->CreateConstraint( pCraneMagnet, pGroup ) )
+ return NULL;
+
+ return pCraneTip;
+}
+