source-engine/game/client/swarm/c_asw_marine.cpp
2023-10-03 17:23:56 +03:00

2452 lines
69 KiB
C++

#include "cbase.h"
#include "c_asw_marine.h"
#include "c_asw_generic_emitter.h"
#include "c_asw_player.h"
#include "c_asw_marine_resource.h"
#include "asw_marine_profile.h"
#include "c_asw_game_resource.h"
#include "c_asw_weapon.h"
#include "fx.h"
#include "asw_fx_shared.h"
#include "eventlist.h"
#include "decals.h"
#include "SoundEmitterSystem/isoundemittersystembase.h"
#include "flashlighteffect.h"
#include "c_asw_door_area.h"
#include "soundenvelope.h"
#include "iasw_client_vehicle.h"
#include "asw_remote_turret_shared.h"
#include "iviewrender_beams.h" // flashlight beam
#include "dlight.h"
#include "iefx.h"
#include "asw_shareddefs.h"
#include "tier0/vprof.h"
#include "bone_setup.h"
#include "c_asw_generic_emitter_entity.h"
#include "datacache/imdlcache.h"
#include "c_asw_fx.h"
#include "fx_water.h"
#include "effect_dispatch_data.h" //for water ripple / splash effect
#include "c_te_effect_dispatch.h" //ditto
#include "c_asw_order_arrow.h"
#include "c_env_projectedtexture.h"
#include "asw_gamerules.h"
#include "cdll_bounded_cvars.h"
#include "c_asw_computer_area.h"
#include "c_asw_button_area.h"
#include "tier1/fmtstr.h"
#include "baseparticleentity.h"
#include "asw_util_shared.h"
#include "asw_melee_system.h"
#include "asw_marine_gamemovement.h"
#include "game_timescale_shared.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//extern ConVar cl_predict;
static ConVar cl_asw_smooth ( "cl_asw_smooth", "1", 0, "Smooth marine's render origin after prediction errors" );
static ConVar cl_asw_smoothtime (
"cl_asw_smoothtime",
"0.1",
0,
"Smooth marine's render origin after prediction error over this many seconds",
true, 0.01, // min/max is 0.01/2.0
true, 2.0
);
ConVar asw_flashlight_dlight_radius("asw_flashlight_dlight_radius", "100", FCVAR_CHEAT, "Radius of the light around the marine.");
ConVar asw_flashlight_dlight_offsetx("asw_flashlight_dlight_offsetx", "30", FCVAR_CHEAT, "Offset of the flashlight dlight");
ConVar asw_flashlight_dlight_offsety("asw_flashlight_dlight_offsety", "0", FCVAR_CHEAT, "Offset of the flashlight dlight");
ConVar asw_flashlight_dlight_offsetz("asw_flashlight_dlight_offsetz", "60", FCVAR_CHEAT, "Offset of the flashlight dlight");
ConVar asw_flashlight_dlight_r("asw_flashlight_dlight_r", "250", FCVAR_CHEAT, "Red component of flashlight colour");
ConVar asw_flashlight_dlight_g("asw_flashlight_dlight_g", "250", FCVAR_CHEAT, "Green component of flashlight colour");
ConVar asw_flashlight_dlight_b("asw_flashlight_dlight_b", "250", FCVAR_CHEAT, "Blue component of flashlight colour");
ConVar asw_marine_ambient("asw_marine_ambient", "0.02", FCVAR_CHEAT, "Ambient light of the marine");
ConVar asw_marine_lightscale("asw_marine_lightscale", "4.0", FCVAR_CHEAT, "Light scale on the marine");
ConVar asw_flashlight_marine_ambient("asw_flashlight_marine_ambient", "0.1", FCVAR_CHEAT, "Ambient light of the marine with flashlight on");
ConVar asw_flashlight_marine_lightscale("asw_flashlight_marine_lightscale", "1.0", FCVAR_CHEAT, "Light scale on the marine with flashlight on");
ConVar asw_left_hand_ik("asw_left_hand_ik", "0", FCVAR_CHEAT, "IK the marine's left hand to his weapon");
ConVar asw_marine_shoulderlight("asw_marine_shoulderlight", "2", 0, "Should marines have a shoulder light effect on them.");
ConVar asw_hide_local_marine("asw_hide_local_marine", "0", FCVAR_CHEAT, "If enabled, your current marine will be invisible");
ConVar asw_override_footstep_volume( "asw_override_footstep_volume", "0", FCVAR_CHEAT, "Overrides footstep volume instead of it being surface dependent" );
ConVar asw_marine_object_motion_blur_scale( "asw_marine_object_motion_blur_scale", "0.0" );
ConVar asw_damage_spark_rate( "asw_damage_spark_rate", "0.24", FCVAR_CHEAT, "Base number of seconds between spark sounds/effects at critical damage." );
extern ConVar asw_DebugAutoAim;
extern float g_fMarinePoisonDuration;
#define FLASHLIGHT_DISTANCE 1000
#define ASW_PROJECTOR_FLASHLIGHT 1
// uncomment to enable a dlight on the marine's flashlight (disabled for perf reasons)
//#define ASW_FLASHLIGHT_DLIGHT
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void RecvProxy_Marine_LocalVelocityX( const CRecvProxyData *pData, void *pStruct, void *pOut );
void RecvProxy_Marine_LocalVelocityY( const CRecvProxyData *pData, void *pStruct, void *pOut );
void RecvProxy_Marine_LocalVelocityZ( const CRecvProxyData *pData, void *pStruct, void *pOut );
//void RecvProxy_Marine_GroundEnt( const CRecvProxyData *pData, void *pStruct, void *pOut );
IMPLEMENT_NETWORKCLASS_ALIASED( ASW_Marine, DT_ASW_Marine )
BEGIN_NETWORK_TABLE( CASW_Marine, DT_ASW_Marine )
RecvPropVectorXY( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ), 0, C_BasePlayer::RecvProxy_LocalOriginXY ),
RecvPropFloat( RECVINFO_NAME( m_vecNetworkOrigin[2], m_vecOrigin[2] ), 0, C_BasePlayer::RecvProxy_LocalOriginZ ),
RecvPropFloat ( RECVINFO( m_vecVelocity[0] ), 0, RecvProxy_Marine_LocalVelocityX ),
RecvPropFloat ( RECVINFO( m_vecVelocity[1] ), 0, RecvProxy_Marine_LocalVelocityY ),
RecvPropFloat ( RECVINFO( m_vecVelocity[2] ), 0, RecvProxy_Marine_LocalVelocityZ ),
RecvPropFloat( RECVINFO_NAME( m_angNetworkAngles[0], m_angRotation[0] ) ),
RecvPropFloat( RECVINFO_NAME( m_angNetworkAngles[1], m_angRotation[1] ) ),
RecvPropFloat( RECVINFO_NAME( m_angNetworkAngles[2], m_angRotation[2] ) ),
RecvPropFloat ( RECVINFO( m_fAIPitch ) ),
RecvPropInt ( RECVINFO( m_fFlags) ),
RecvPropInt ( RECVINFO( m_iHealth) ),
RecvPropInt ( RECVINFO( m_iMaxHealth ) ),
RecvPropFloat ( RECVINFO( m_fInfestedTime ) ),
RecvPropFloat ( RECVINFO( m_fInfestedStartTime ) ),
RecvPropInt ( RECVINFO( m_ASWOrders), 4),
RecvPropEHandle ( RECVINFO( m_Commander) ),
RecvPropArray3 ( RECVINFO_ARRAY( m_iAmmo ), RecvPropInt( RECVINFO( m_iAmmo[0] ) ) ),
RecvPropBool ( RECVINFO( m_bSlowHeal ) ),
RecvPropInt ( RECVINFO( m_iSlowHealAmount ) ),
RecvPropBool ( RECVINFO( m_bPreventMovement ) ),
RecvPropBool ( RECVINFO( m_bWalking ) ),
RecvPropFloat ( RECVINFO( m_fFFGuardTime ) ),
RecvPropEHandle ( RECVINFO( m_hUsingEntity ) ),
RecvPropVector ( RECVINFO( m_vecFacingPointFromServer ) ),
RecvPropEHandle ( RECVINFO( m_hGroundEntity ) ), // , RecvProxy_Marine_GroundEnt
RecvPropEHandle ( RECVINFO( m_hMarineFollowTarget ) ),
RecvPropTime ( RECVINFO(m_fStopMarineTime) ),
RecvPropTime ( RECVINFO(m_fNextMeleeTime) ),
RecvPropTime ( RECVINFO( m_flNextAttack ) ),
RecvPropInt ( RECVINFO( m_iMeleeAttackID ) ),
RecvPropEHandle ( RECVINFO ( m_hCurrentHack ) ),
RecvPropBool ( RECVINFO ( m_bOnFire ) ),
//emotes
RecvPropBool (RECVINFO(bEmoteMedic)),
RecvPropBool (RECVINFO(bEmoteAmmo)),
RecvPropBool (RECVINFO(bEmoteSmile)),
RecvPropBool (RECVINFO(bEmoteStop)),
RecvPropBool (RECVINFO(bEmoteGo)),
RecvPropBool (RECVINFO(bEmoteExclaim)),
RecvPropBool (RECVINFO(bEmoteAnimeSmile)),
RecvPropBool (RECVINFO(bEmoteQuestion)),
// driving
RecvPropEHandle (RECVINFO(m_hASWVehicle)),
RecvPropBool (RECVINFO(m_bDriving)),
RecvPropBool (RECVINFO(m_bIsInVehicle)),
// falling over
RecvPropBool (RECVINFO(m_bKnockedOut)),
// turret
RecvPropEHandle( RECVINFO ( m_hRemoteTurret ) ),
// We send all the marine's weapons to all the other marines
RecvPropArray3( RECVINFO_ARRAY(m_hMyWeapons), RecvPropEHandle( RECVINFO( m_hMyWeapons[0] ) ) ),
RecvPropFloat( RECVINFO(m_vecViewOffset[0]) ),
RecvPropFloat( RECVINFO(m_vecViewOffset[1]) ),
RecvPropFloat( RECVINFO(m_vecViewOffset[2]) ),
#ifdef MELEE_CHARGE_ATTACKS
RecvPropFloat ( RECVINFO( m_flMeleeHeavyKeyHoldStart ) ),
#endif
RecvPropInt( RECVINFO( m_iForcedActionRequest ) ),
RecvPropBool ( RECVINFO( m_bReflectingProjectiles ) ),
RecvPropTime( RECVINFO( m_flDamageBuffEndTime ) ),
RecvPropTime( RECVINFO( m_flElectrifiedArmorEndTime ) ),
RecvPropInt ( RECVINFO( m_iPowerupType ) ),
RecvPropTime ( RECVINFO( m_flPowerupExpireTime ) ),
RecvPropBool ( RECVINFO( m_bPowerupExpires ) ),
RecvPropFloat ( RECVINFO( m_flKnockdownYaw ) ),
RecvPropFloat ( RECVINFO( m_flMeleeYaw ) ),
RecvPropBool ( RECVINFO( m_bFaceMeleeYaw ) ),
RecvPropFloat ( RECVINFO( m_flPreventLaserSightTime ) ),
RecvPropBool ( RECVINFO( m_bAICrouch ) ),
RecvPropInt ( RECVINFO( m_iJumpJetting ) ),
END_RECV_TABLE()
BEGIN_PREDICTION_DATA( C_ASW_Marine )
//DEFINE_PRED_FIELD( m_vecVelocity, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ),
DEFINE_PRED_FIELD( m_flCycle, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ),
//DEFINE_PRED_FIELD( m_flAnimTime, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ),
DEFINE_PRED_FIELD( m_nSequence, FIELD_INTEGER, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ),
DEFINE_PRED_FIELD( m_nNewSequenceParity, FIELD_INTEGER, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ),
DEFINE_PRED_FIELD( m_nResetEventsParity, FIELD_INTEGER, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ),
DEFINE_PRED_FIELD( m_hGroundEntity, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD_TOL( m_fStopMarineTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
DEFINE_PRED_FIELD_TOL( m_fNextMeleeTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
DEFINE_PRED_FIELD_TOL( m_flNextAttack, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
DEFINE_PRED_FIELD( m_iEFlags, FIELD_INTEGER, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ),
DEFINE_PRED_FIELD( m_iMeleeAttackID, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
#ifdef MELEE_CHARGE_ATTACKS
DEFINE_PRED_FIELD_TOL( m_flMeleeHeavyKeyHoldStart, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
#endif
DEFINE_PRED_FIELD( m_iForcedActionRequest, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_bReflectingProjectiles, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_flMeleeYaw, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_bFaceMeleeYaw, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_bWalking, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD_TOL( m_flPreventLaserSightTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
DEFINE_PRED_FIELD( m_iJumpJetting, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
DEFINE_FIELD( m_nOldButtons, FIELD_INTEGER ),
DEFINE_FIELD( m_surfaceFriction, FIELD_FLOAT ),
DEFINE_FIELD( m_vecMeleeStartPos, FIELD_VECTOR ),
DEFINE_FIELD( m_flMeleeStartTime, FIELD_FLOAT ),
DEFINE_FIELD( m_flMeleeLastCycle, FIELD_FLOAT ),
DEFINE_FIELD( m_bMeleeCollisionDamage, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bMeleeComboKeypressAllowed, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bMeleeComboKeyPressed, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bMeleeComboTransitionAllowed, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bMeleeMadeContact, FIELD_BOOLEAN ),
DEFINE_FIELD( m_iUsableItemsOnMeleePress, FIELD_INTEGER ),
DEFINE_FIELD( m_iMeleeAllowMovement, FIELD_INTEGER ),
DEFINE_FIELD( m_bMeleeKeyReleased, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bPlayedMeleeHitSound, FIELD_BOOLEAN ),
#ifdef MELEE_CHARGE_ATTACKS
DEFINE_FIELD( m_bMeleeHeavyKeyHeld, FIELD_BOOLEAN ),
#endif
DEFINE_FIELD( m_bMeleeChargeActivate, FIELD_BOOLEAN ),
DEFINE_AUTO_ARRAY( m_iPredictedEvent, FIELD_INTEGER ),
DEFINE_AUTO_ARRAY( m_flPredictedEventTime, FIELD_FLOAT ),
DEFINE_FIELD( m_iNumPredictedEvents, FIELD_INTEGER ),
DEFINE_FIELD( m_iOnLandMeleeAttackID, FIELD_INTEGER ),
DEFINE_FIELD( m_vecJumpJetStart, FIELD_VECTOR ),
DEFINE_FIELD( m_vecJumpJetEnd, FIELD_VECTOR ),
DEFINE_FIELD( m_flJumpJetStartTime, FIELD_FLOAT ),
DEFINE_FIELD( m_flJumpJetEndTime, FIELD_FLOAT ),
/*
DEFINE_FIELD( m_bSlowHeal, FIELD_BOOLEAN ),
DEFINE_FIELD( m_vecFacingPointFromServer, FIELD_VECTOR ),
DEFINE_FIELD( m_hRemoteTurret, FIELD_EHANDLE ),
DEFINE_FIELD( m_hASWVehicle, FIELD_EHANDLE ),
DEFINE_FIELD( m_bDriving, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bIsInVehicle, FIELD_BOOLEAN ),
DEFINE_FIELD( bEmoteMedic, FIELD_BOOLEAN ),
DEFINE_FIELD( bEmoteAmmo, FIELD_BOOLEAN ),
DEFINE_FIELD( bEmoteStop, FIELD_BOOLEAN ),
DEFINE_FIELD( bEmoteGo, FIELD_BOOLEAN ),
DEFINE_FIELD( bEmoteExclaim, FIELD_BOOLEAN ),
DEFINE_FIELD( bEmoteAnimeSmile, FIELD_BOOLEAN ),
DEFINE_FIELD( bEmoteQuestion, FIELD_BOOLEAN ),
DEFINE_FIELD( m_Commander, FIELD_EHANDLE),
DEFINE_FIELD( m_fStopFacingPointTime, FIELD_FLOAT ),
DEFINE_FIELD( m_bHacking, FIELD_BOOLEAN ),
DEFINE_FIELD( m_hCurrentHack, FIELD_EHANDLE),
DEFINE_FIELD( m_hUsingEntity, FIELD_EHANDLE),
DEFINE_FIELD( m_fLastTurningYaw, FIELD_FLOAT),
DEFINE_FIELD( m_vecLastRenderedPos, FIELD_VECTOR),
DEFINE_FIELD( m_bUseLastRenderedEyePosition, FIELD_BOOLEAN),
// extra test
//DEFINE_FIELD( m_vecCustomRenderOrigin, FIELD_VECTOR),
//DEFINE_FIELD( m_vecPredictionError, FIELD_VECTOR),
//DEFINE_FIELD( m_flPredictionErrorTime, FIELD_FLOAT),
//DEFINE_FIELD( m_fAIPitch, FIELD_FLOAT),
//DEFINE_FIELD( m_AIEyeAngles, FIELD_VECTOR),
DEFINE_FIELD( m_hLastWeaponSwitchedTo, FIELD_EHANDLE),
DEFINE_FIELD( m_ShadowDirection, FIELD_VECTOR),
DEFINE_FIELD( m_hMarineResource, FIELD_EHANDLE),
DEFINE_FIELD( m_CurrentBlipStrength, FIELD_FLOAT),
DEFINE_FIELD( m_CurrentBlipDirection, FIELD_INTEGER),
DEFINE_FIELD( m_LastThinkTime, FIELD_FLOAT),
DEFINE_FIELD( m_hMarineFollowTarget, FIELD_EHANDLE),
DEFINE_FIELD( m_bPreventMovement, FIELD_BOOLEAN),
DEFINE_FIELD( m_ASWOrders, FIELD_INTEGER),
DEFINE_FIELD( m_hOrderArrow, FIELD_EHANDLE),
DEFINE_FIELD( bPlayingFlamerSound, FIELD_BOOLEAN),
DEFINE_FIELD( m_fFlameTime, FIELD_FLOAT),
DEFINE_SOUNDPATCH( m_pFlamerLoopSound),
DEFINE_FIELD( bPlayingFireExtinguisherSound, FIELD_BOOLEAN),
DEFINE_FIELD( m_fFireExtinguisherTime, FIELD_FLOAT),
DEFINE_SOUNDPATCH( m_pFireExtinguisherLoopSound ),
DEFINE_FIELD( m_fNextHeartbeat, FIELD_FLOAT),
DEFINE_FIELD( m_fFFGuardTime, FIELD_FLOAT),
DEFINE_FIELD( m_iMaxHealth, FIELD_INTEGER),
DEFINE_FIELD( m_bOnFire, FIELD_BOOLEAN),
DEFINE_FIELD( m_bClientOnFire, FIELD_BOOLEAN),
DEFINE_FIELD( m_fInfestedTime, FIELD_FLOAT),
DEFINE_FIELD( m_fInfestedStartTime, FIELD_FLOAT),
DEFINE_FIELD( m_fRedNamePulse, FIELD_FLOAT),
DEFINE_FIELD( m_bRedNamePulseUp, FIELD_BOOLEAN),
DEFINE_FIELD( m_vecFacingPoint, FIELD_VECTOR),
DEFINE_FIELD( bEmoteSmile, FIELD_BOOLEAN),
DEFINE_FIELD( bClientEmoteMedic, FIELD_BOOLEAN),
DEFINE_FIELD( bClientEmoteAmmo, FIELD_BOOLEAN),
DEFINE_FIELD( bClientEmoteSmile, FIELD_BOOLEAN),
DEFINE_FIELD( bClientEmoteStop, FIELD_BOOLEAN),
DEFINE_FIELD( bClientEmoteGo, FIELD_BOOLEAN),
DEFINE_FIELD( bClientEmoteExclaim, FIELD_BOOLEAN),
DEFINE_FIELD( bClientEmoteAnimeSmile, FIELD_BOOLEAN),
DEFINE_FIELD( bClientEmoteQuestion, FIELD_BOOLEAN),
DEFINE_FIELD( fEmoteMedicTime, FIELD_FLOAT),
DEFINE_FIELD( fEmoteAmmoTime, FIELD_FLOAT),
DEFINE_FIELD( fEmoteSmileTime, FIELD_FLOAT),
DEFINE_FIELD( fEmoteStopTime, FIELD_FLOAT),
DEFINE_FIELD( fEmoteGoTime, FIELD_FLOAT),
DEFINE_FIELD( fEmoteExclaimTime, FIELD_FLOAT),
DEFINE_FIELD( fEmoteAnimeSmileTime, FIELD_FLOAT),
DEFINE_FIELD( fEmoteQuestionTime, FIELD_FLOAT),
DEFINE_FIELD( m_bHasClientsideVehicle, FIELD_BOOLEAN),
DEFINE_FIELD( m_bKnockedOut, FIELD_BOOLEAN),
DEFINE_FIELD( m_fPoison, FIELD_FLOAT),
DEFINE_FIELD( m_hShoulderCone, FIELD_EHANDLE),
DEFINE_FIELD( m_fLastYawHack, FIELD_FLOAT),
DEFINE_FIELD( m_fLastPitchHack, FIELD_FLOAT),
DEFINE_FIELD( m_bStepSideLeft, FIELD_BOOLEAN),
*/
/*
DEFINE_FIELD( CUtlVector < EHANDLE > m_TouchingDoors'
DEFINE_FIELD( IASW_Client_Vehicle* m_pClientsideVehicle'
DEFINE_FIELD( IASWPlayerAnimState *m_PlayerAnimState'
DEFINE_FIELD( Beam_t *m_pFlashlightBeam'
DEFINE_FIELD( dlight_t* m_pFlashlightDLight'
DEFINE_FIELD( CSmartPtr < CASWGenericEmitter > m_hFlameEmitter'
DEFINE_FIELD( CSmartPtr < CASWGenericEmitter > m_hFlameStreamEmitter'
DEFINE_FIELD( CSmartPtr < CASWGenericEmitter > m_hFireExtinguisherEmitter'
DEFINE_FIELD( CSmartPtr < CASWGenericEmitter > m_hHealEmitter'
DEFINE_FIELD( CNewParticleEffect *m_pBurningEffect'
*/
END_PREDICTION_DATA()
/*
void RecvProxy_Marine_GroundEnt( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
C_ASW_Marine *pMarine = (C_ASW_Marine *) pStruct;
Assert( pMarine );
EHANDLE hTarget;
RecvProxy_IntToEHandle( pData, pStruct, &hTarget );
pMarine->m_hGroundEntity.Init( hTarget.GetEntryIndex(), hTarget.GetSerialNumber() );
}
*/
void RecvProxy_Marine_LocalVelocityX( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
C_ASW_Marine *pMarine = (C_ASW_Marine *) pStruct;
Assert( pMarine );
float flNewVel_x = pData->m_Value.m_Float;
Vector vecVelocity = pMarine->GetLocalVelocity();
if( vecVelocity.x != flNewVel_x ) // Should this use an epsilon check?
{
//if (vecVelocity.x > 30.0f)
//{
//}
vecVelocity.x = flNewVel_x;
pMarine->SetLocalVelocity( vecVelocity );
}
}
void RecvProxy_Marine_LocalVelocityY( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
C_ASW_Marine *pMarine = (C_ASW_Marine *) pStruct;
Assert( pMarine );
float flNewVel_y = pData->m_Value.m_Float;
Vector vecVelocity = pMarine->GetLocalVelocity();
if( vecVelocity.y != flNewVel_y )
{
vecVelocity.y = flNewVel_y;
pMarine->SetLocalVelocity( vecVelocity );
}
}
void RecvProxy_Marine_LocalVelocityZ( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
C_ASW_Marine *pMarine = (C_ASW_Marine *) pStruct;
Assert( pMarine );
float flNewVel_z = pData->m_Value.m_Float;
Vector vecVelocity = pMarine->GetLocalVelocity();
if( vecVelocity.z != flNewVel_z )
{
vecVelocity.z = flNewVel_z;
pMarine->SetLocalVelocity( vecVelocity );
}
}
C_ASW_Marine::C_ASW_Marine() :
m_MotionBlurObject( this, asw_marine_object_motion_blur_scale.GetFloat() ),
m_vLaserSightCorrection( 0, 0, 0 ),
m_flLaserSightLength( 0 )
{
m_hShoulderCone = NULL;
m_PlayerAnimState = CreatePlayerAnimState(this, this, LEGANIM_9WAY, false);
SetPredictionEligible( true );
m_Commander = NULL;
m_hMarineResource = NULL;
m_fPoison = 0;
m_ShadowDirection.x = 0;
m_ShadowDirection.y = 0;
m_ShadowDirection.z = -1;
m_CurrentBlipStrength = 0;
m_CurrentBlipDirection = 1;
m_LastThinkTime = gpGlobals->curtime;
m_fLastYawHack = m_fLastPitchHack = 0;
m_bStepSideLeft = true;
m_nOldButtons = 0;
//m_fAmbientLight = asw_marine_ambient.GetFloat();
//m_fLightingScale = asw_marine_lightscale.GetFloat();
m_pFlashlight = NULL;
m_TouchingDoors.RemoveAll();
bPlayingFlamerSound = false;
bPlayingFireExtinguisherSound = false;
m_pFlamerLoopSound = 0;
m_fFlameTime = 0;
m_flNextDamageSparkTime = 0.0f;
m_bClientElectrifiedArmor = false;
m_fFireExtinguisherTime = 0;
m_fNextHeartbeat = 0;
m_iOldHealth = m_iHealth;
m_fLastHealTime = 0.0f;
m_vecFacingPoint = vec3_origin;
m_fStopFacingPointTime = 0;
m_fStopMarineTime = 0;
m_bHasClientsideVehicle = false;
m_vecPredictionError.Init();
m_flPredictionErrorTime = 0;
m_pFlashlightBeam = NULL;
m_pFlashlightDLight = NULL;
m_fNextMeleeTime = 0;
#ifdef MELEE_CHARGE_ATTACKS
m_flMeleeHeavyKeyHoldStart = 0;
#endif
m_bClientOnFire = false;
m_pBurningEffect = NULL;
m_pJumpJetEffect[0] = NULL;
m_pJumpJetEffect[1] = NULL;
m_hOrderArrow = C_ASW_Order_Arrow::CreateOrderArrow();
m_fRedNamePulse = 0;
m_bRedNamePulseUp = true;
bClientEmoteMedic = bClientEmoteAmmo = bClientEmoteSmile = bClientEmoteStop
= bClientEmoteGo = bClientEmoteExclaim = bClientEmoteAnimeSmile = bClientEmoteQuestion = false;
fEmoteMedicTime = fEmoteAmmoTime = fEmoteSmileTime = fEmoteStopTime
= fEmoteGoTime = fEmoteExclaimTime = fEmoteAnimeSmileTime = fEmoteQuestionTime = 0;
m_surfaceProps = 0;
m_pSurfaceData = NULL;
m_surfaceFriction = 1.0f;
m_chTextureType = m_chPreviousTextureType = 0;
m_iPowerupType = -1;
m_flPowerupExpireTime = -1;
m_bPowerupExpires = false;
}
C_ASW_Marine::~C_ASW_Marine()
{
m_PlayerAnimState->Release();
if (m_pFlashlight)
{
delete m_pFlashlight;
}
StopFlamerLoop();
StopFireExtinguisherLoop();
m_bOnFire = false;
UpdateFireEmitters();
if (m_hOrderArrow.Get())
m_hOrderArrow->Release();
if ( m_hLowHeathEffect )
{
m_hLowHeathEffect->StopEmission(false, false , true);
m_hLowHeathEffect = NULL;
}
if ( m_hCriticalHeathEffect )
{
m_hCriticalHeathEffect->StopEmission(false, false , true);
m_hCriticalHeathEffect = NULL;
}
if ( m_hSentryBuildDisplay )
{
m_hSentryBuildDisplay->StopEmission(false, false , true);
m_hSentryBuildDisplay = NULL;
}
}
bool C_ASW_Marine::ShouldPredict()
{
if (C_BasePlayer::IsLocalPlayer(GetCommander()))
{
FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh )
{
ACTIVE_SPLITSCREEN_PLAYER_GUARD( hh );
C_ASW_Player* player = C_ASW_Player::GetLocalASWPlayer();
if (player && player->GetMarine() == this)
{
return true;
}
}
}
return false;
}
C_BasePlayer *C_ASW_Marine::GetPredictionOwner()
{
return GetCommander();
}
void C_ASW_Marine::PhysicsSimulate( void )
{
if (ShouldPredict())
{
m_nSimulationTick = gpGlobals->tickcount;
return;
}
BaseClass::PhysicsSimulate();
}
void C_ASW_Marine::InitPredictable( C_BasePlayer *pOwner )
{
SetLocalVelocity(vec3_origin);
BaseClass::InitPredictable( pOwner );
}
void C_ASW_Marine::PostDataUpdate( DataUpdateType_t updateType )
{
bool bPredict = ShouldPredict();
if ( bPredict )
{
SetSimulatedEveryTick( true );
}
else
{
SetSimulatedEveryTick( false );
// estimate velocity for non local players
float flTimeDelta = m_flSimulationTime - m_flOldSimulationTime;
if ( flTimeDelta > 0 && !IsEffectActive(EF_NOINTERP) )
{
Vector newVelo = (GetNetworkOrigin() - GetOldOrigin() ) / flTimeDelta;
SetAbsVelocity( newVelo);
}
}
// if player has switched into this marine, set it to be prediction eligible
if (bPredict)
{
// C_BaseEntity assumes we're networking the entity's angles, so pretend that it
// networked the same value we already have.
//SetNetworkAngles( GetLocalAngles() );
SetPredictionEligible( true );
}
else
{
SetPredictionEligible( false );
}
BaseClass::PostDataUpdate( updateType );
if ( GetPredictable() && !bPredict )
{
MDLCACHE_CRITICAL_SECTION();
ShutdownPredictable();
}
}
void C_ASW_Marine::UpdateClientSideAnimation()
{
VPROF_BUDGET( "C_ASW_Marine::UpdateClientSideAnimation", VPROF_BUDGETGROUP_ASW_CLIENT );
if ( GetSequence() != -1 )
{
// latch old values
//OnLatchInterpolatedVariables( LATCH_ANIMATION_VAR );
// move frame forward
FrameAdvance( gpGlobals->frametime );
}
C_ASW_Player *pPlayer = GetCommander();
if ( pPlayer && C_BasePlayer::IsLocalPlayer(pPlayer) && pPlayer->GetMarine() == this && !IsControllingTurret())
{
m_PlayerAnimState->Update( pPlayer->EyeAngles()[YAW], pPlayer->EyeAngles()[PITCH] );
m_fLastYawHack = pPlayer->EyeAngles()[YAW];
m_fLastPitchHack = pPlayer->EyeAngles()[PITCH];
}
else
m_PlayerAnimState->Update( ASWEyeAngles()[YAW], ASWEyeAngles()[PITCH] );
if ( GetSequence() != -1 )
{
// latch old values
OnLatchInterpolatedVariables( LATCH_ANIMATION_VAR );
}
}
const Vector& C_ASW_Marine::GetRenderOrigin()
{
m_vecCustomRenderOrigin = GetAbsOrigin();
/*
if (m_PlayerAnimState && !m_PlayerAnimState->IsAnimatingJump())
{
C_BaseEntity *pEnt = cl_entitylist->FirstBaseEntity();
while (pEnt)
{
if (FClassnameIs(pEnt, "class C_DynamicProp"))
{
//Msg("Setting z to %f\n", pEnt->GetAbsOrigin().z + 10);
m_vecCustomRenderOrigin.z = pEnt->GetAbsOrigin().z + 10;
break;
}
pEnt = cl_entitylist->NextBaseEntity( pEnt );
}
}
*/
if (IsInhabited())
{
Vector vSmoothOffset;
GetPredictionErrorSmoothingVector( vSmoothOffset );
m_vecCustomRenderOrigin += vSmoothOffset;
}
return m_vecCustomRenderOrigin;
}
void C_ASW_Marine::NotePredictionError( const Vector &vDelta )
{
Vector vOldDelta;
GetPredictionErrorSmoothingVector( vOldDelta );
// sum all errors within smoothing time
m_vecPredictionError = vDelta + vOldDelta;
// remember when last error happened
m_flPredictionErrorTime = gpGlobals->curtime;
ResetLatched();
}
void C_ASW_Marine::GetPredictionErrorSmoothingVector( Vector &vOffset )
{
if ( engine->IsPlayingDemo() || !cl_asw_smooth.GetInt() || !cl_predict->GetBool() )
{
vOffset.Init();
return;
}
float errorAmount = ( gpGlobals->curtime - m_flPredictionErrorTime ) / cl_asw_smoothtime.GetFloat();
if ( errorAmount >= 1.0f )
{
vOffset.Init();
return;
}
errorAmount = 1.0f - errorAmount;
vOffset = m_vecPredictionError * errorAmount;
}
CBaseCombatWeapon* C_ASW_Marine::ASWAnim_GetActiveWeapon()
{
return GetActiveWeapon();
}
CASW_Marine_Profile* C_ASW_Marine::GetMarineProfile()
{
C_ASW_Marine_Resource* pMR = GetMarineResource();
if (!pMR)
return NULL;
return pMR->GetProfile();
}
bool C_ASW_Marine::ASWAnim_CanMove()
{
return true;
}
const QAngle& C_ASW_Marine::GetRenderAngles()
{
//if (ShouldPredict())
{
if ( IsRagdoll() )
{
return vec3_angle;
}
else
{
return m_PlayerAnimState->GetRenderAngles();
}
}
//else
//{
//return BaseClass::GetRenderAngles();
//}
}
C_ASW_Marine_Resource* C_ASW_Marine::GetMarineResource()
{
if (m_hMarineResource.Get() != NULL)
return m_hMarineResource.Get();
// find our marine info
C_ASW_Game_Resource* pGameResource = ASWGameResource();
if (pGameResource != NULL)
{
for (int i=0;i<pGameResource->GetMaxMarineResources();i++)
{
C_ASW_Marine_Resource* pMR = pGameResource->GetMarineResource(i);
if (pMR != NULL && pMR->GetMarineEntity() == this)
{
m_hMarineResource = pMR;
return pMR;
}
}
}
return NULL;
}
// shadow direction test
bool C_ASW_Marine::GetShadowCastDirection( Vector *pDirection, ShadowType_t shadowType ) const
{
return false;
pDirection->x = m_ShadowDirection.x;
pDirection->y = m_ShadowDirection.y;
pDirection->z = m_ShadowDirection.z;
return true;
}
void C_ASW_Marine::ClientThink()
{
VPROF_BUDGET( "C_ASW_Marine::ClientThink", VPROF_BUDGETGROUP_ASW_CLIENT );
BaseClass::ClientThink();
if ( ShouldPredict() )
{
// Update projected texture culling helper
C_EnvProjectedTexture::SetVisibleBBoxMinHeight( GetAbsOrigin().z - 64.0f );
}
if (IsInhabited())
{
//m_vecLastRenderedPos = GetRenderOrigin();
}
if (asw_DebugAutoAim.GetInt() == 3)
{
Msg("%f: Drawmodel render origin %s\n", gpGlobals->curtime, VecToString(GetRenderOrigin()));
}
//if (GetActiveWeapon() && GetActiveWeapon()->GetPrimaryAmmoType())
//Msg("Ammo = %d\n", GetAmmoCount(GetActiveWeapon()->GetPrimaryAmmoType()));
//Msg("Ammo = %d\n", GetAmmoCount("ASW_R"));
// tick up/down our minimap blip, reversing direction at the bounds
float deltatime = gpGlobals->curtime - m_LastThinkTime;
m_CurrentBlipStrength += m_CurrentBlipDirection * deltatime * 3;
if (m_CurrentBlipStrength > 1)
{
m_CurrentBlipStrength = 1;
m_CurrentBlipDirection = - m_CurrentBlipDirection;
}
else if (m_CurrentBlipStrength < 0)
{
m_CurrentBlipStrength = 0;
m_CurrentBlipDirection = - m_CurrentBlipDirection;
}
if ( IsInfested() )
{
// Predict
m_fInfestedTime -= deltatime;
}
//Vector light_pos = engine->GetClosestLightLocation(GetAbsOrigin());
//m_ShadowDirection.x = RandomFloat( -1.0, 1.0 );
//m_ShadowDirection.y = RandomFloat( -1.0, 1.0 );
//m_ShadowDirection.z = RandomFloat( -1.0, 1.0 );
//VectorSubtract(GetAbsOrigin(), light_pos, m_ShadowDirection);
//m_ShadowDirection = engine->GetClosestLightPosition(GetAbsOrigin());
//m_ShadowDirection.NormalizeInPlace();
// turn the flame emitter on or off
C_ASW_Weapon *pWeapon = GetActiveASWWeapon();
// flamethrower handles its own effects now
if ( pWeapon && IsAlive() )//m_hFlameEmitter.IsValid() &&
{
bool bFlameOn = pWeapon->ShouldMarineFlame();
if (bFlameOn && !bPlayingFlamerSound)
{
StartFlamerLoop();
}
else if (!bFlameOn && bPlayingFlamerSound)
{
StopFlamerLoop();
}
bPlayingFlamerSound = bFlameOn;
/*
m_hFlameEmitter->SetActive(bFlameOn);
Vector vecMuzzle = GetAbsOrigin()+Vector(0,0,45);
QAngle angMuzzle;
C_BaseAnimating::PushAllowBoneAccess( true, false, "ClientThink" );
pWeapon->GetAttachment( pWeapon->LookupAttachment("muzzle"), vecMuzzle, angMuzzle );
m_hFlameEmitter->Think(gpGlobals->frametime, vecMuzzle, angMuzzle);
if (m_hFlameStreamEmitter.IsValid())
{
m_hFlameStreamEmitter->SetActive(bFlameOn);
m_hFlameStreamEmitter->Think(gpGlobals->frametime, vecMuzzle, angMuzzle);
}
C_BaseAnimating::PopBoneAccess( "ClientThink" );
*/
}
else
{
if (bPlayingFlamerSound)
StopFlamerLoop();
}
if ( pWeapon && IsAlive())
{
//bool bFireExtinguisherOn = m_fFireExtinguisherTime >= gpGlobals->curtime;
bool bFireExtinguisherOn = pWeapon->ShouldMarineFireExtinguish();
if (bFireExtinguisherOn && !bPlayingFireExtinguisherSound)
{
StartFireExtinguisherLoop();
}
else if (!bFireExtinguisherOn && bPlayingFireExtinguisherSound)
{
StopFireExtinguisherLoop();
}
bPlayingFireExtinguisherSound = bFireExtinguisherOn;
// extinguisher weapons now handle their own effects
/*
m_hFireExtinguisherEmitter->SetActive(bFireExtinguisherOn);
Vector vecMuzzle = GetAbsOrigin()+Vector(0,0,45);
QAngle angMuzzle;
int iMuzzleAttach = pWeapon->LookupAttachment("muzzle");
//Msg("Firext muzzle attach = %d\n", iMuzzleAttach);
if (!pWeapon->GetAttachment(iMuzzleAttach , vecMuzzle, angMuzzle ))
{
vecMuzzle = GetAbsOrigin()+Vector(0,0,45);
angMuzzle = ASWEyeAngles();
}
else
{
//Msg(" from weapon %s vec = %s ang = %s\n", pWeapon->GetClassname(), VecToString(vecMuzzle), VecToString(angMuzzle));
}
m_hFireExtinguisherEmitter->Think(gpGlobals->frametime, vecMuzzle, angMuzzle);
//m_hFireExtinguisherEmitter->Think(gpGlobals->frametime, GetAbsOrigin()+Vector(0,0,45), ASWEyeAngles());
*/
}
else
{
if (bPlayingFireExtinguisherSound)
StopFireExtinguisherLoop();
}
// if we're healing and we don't have an emitter, create a heal emitter
if ( m_bSlowHeal && GetHealth()>0 )
{
if ( !m_pHealEmitter )
m_pHealEmitter = ParticleProp()->Create( "heal_effect", PATTACH_ABSORIGIN_FOLLOW, -1, Vector( 0, 0, 50 ) );
}
else if ( m_pHealEmitter ) //we aren't healing, but the emitter is active, kill it
{
m_pHealEmitter->StopEmission(false, false, true);
m_pHealEmitter = NULL;
}
if (m_hShoulderCone.Get() && ( GetHealth()<=0 || IsEffectActive(EF_NODRAW)) )
{
UTIL_Remove( m_hShoulderCone );
m_hShoulderCone = NULL;
}
if (ShouldPredict())
{
UpdateHeartbeat();
}
if ( IsAlive() )
{
UpdateDamageEffects( false );
}
if (m_vecFacingPoint!=vec3_origin && gpGlobals->curtime > m_fStopFacingPointTime)
{
m_vecFacingPoint = vec3_origin;
}
if (IsInhabited() && ShouldPredict())
{
g_fMarinePoisonDuration = m_fPoison;
m_fPoison -= gpGlobals->frametime;
}
TickEmotes(deltatime);
TickRedName(deltatime);
UpdateFireEmitters();
UpdateJumpJetEffects();
UpdateElectrifiedArmor();
//SetNextClientThink( gpGlobals->curtime + 0.1f );
m_LastThinkTime = gpGlobals->curtime;
}
void C_ASW_Marine::DoWaterRipples()
{
Vector knee = GetAbsOrigin() + Vector(0, 0, 12);
if ( enginetrace->GetPointContents( knee ) & MASK_WATER )
{
// spawn water ripples if we're in water
//run splash
CEffectData data;
//trace up from foot position to the water surface
trace_t tr;
Vector origin = GetAbsOrigin();
Vector vecTrace(0,0,1024);
UTIL_TraceLine( origin, origin + vecTrace, MASK_WATER, NULL, COLLISION_GROUP_NONE, &tr );
if ( tr.fractionleftsolid )
{
data.m_vOrigin = origin + (vecTrace * tr.fractionleftsolid);
}
else
{
data.m_vOrigin = origin;
}
data.m_vNormal = Vector( 0,0,1 );
data.m_flScale = random->RandomFloat( 4.0f, 5.0f ) * 2.0f;
DispatchEffect( "aswwatersplash", data );
//static Vector s_MarineWaterSplashColor( 0.5, 0.5, 0.5 );
//FX_ASWWaterRipple(data.m_vOrigin, 1.0f, &s_MarineWaterSplashColor, 1.5f, 0.1f);
}
}
void C_ASW_Marine::SetClientsideVehicle(IASW_Client_Vehicle* pVehicle)
{
m_pClientsideVehicle = pVehicle;
m_bHasClientsideVehicle = (m_pClientsideVehicle != NULL);
}
void C_ASW_Marine::CreateWeaponEmitters()
{
// flamethrower weapon handles its own effects now
/*
m_hFlameEmitter = CASWGenericEmitter::Create( "asw_emitter" );
if ( m_hFlameEmitter.IsValid() )
{
m_hFlameEmitter->UseTemplate("flamer5");
m_hFlameEmitter->SetActive(false);
m_hFlameEmitter->m_hCollisionIgnoreEntity = this;
m_hFlameEmitter->SetCustomCollisionGroup(ASW_COLLISION_GROUP_IGNORE_NPCS);
//m_hFlameEmitter->SetGlowMaterial("swarm/sprites/aswredglow2");
}
else
{
Warning("Failed to create a marine's flame emitter\n");
}
m_hFlameStreamEmitter = CASWGenericEmitter::Create( "asw_emitter" );
if ( m_hFlameStreamEmitter.IsValid() )
{
m_hFlameStreamEmitter->UseTemplate("flamerstream1");
m_hFlameStreamEmitter->SetActive(false);
m_hFlameStreamEmitter->m_hCollisionIgnoreEntity = this;
m_hFlameStreamEmitter->SetCustomCollisionGroup(ASW_COLLISION_GROUP_IGNORE_NPCS);
//m_hFlameEmitter->SetGlowMaterial("swarm/sprites/aswredglow2");
}
else
{
Warning("Failed to create a marine's flame stream emitter\n");
}*/
// fire extinguisher weapons now handle their own effects
/*
m_hFireExtinguisherEmitter = CASWGenericEmitter::Create( "asw_emitter" );
if ( m_hFireExtinguisherEmitter.IsValid() )
{
m_hFireExtinguisherEmitter->UseTemplate("fireextinguisher2");
m_hFireExtinguisherEmitter->SetActive(false);
m_hFireExtinguisherEmitter->m_hCollisionIgnoreEntity = this;
}
else
{
Warning("Failed to create a marine's fire extinguisher emitter\n");
}
*/
}
/*
void C_ASW_Marine::CreateHealEmitter()
{
m_hHealEmitter = CASWGenericEmitter::Create( "asw_emitter" );
if ( m_hHealEmitter.IsValid() )
{
m_hHealEmitter->UseTemplate("healfast");
m_hHealEmitter->SetActive(false);
}
else
{
Warning("Failed to create a marine's heal emitter\n");
}
}
*/
void C_ASW_Marine::OnDataChanged( DataUpdateType_t updateType )
{
BaseClass::OnDataChanged( updateType );
if ( updateType == DATA_UPDATE_CREATED )
{
CreateWeaponEmitters();
//CreateHealEmitter();
CreateShoulderCone();
// We want to think every frame.
SetNextClientThink( CLIENT_THINK_ALWAYS );
return;
}
if ( m_bClientSideRagdoll && m_pClientsideRagdoll )
{
ASWGameRules()->m_hMarineDeathRagdoll = m_pClientsideRagdoll;
}
if ( m_iOldHealth != m_iHealth )
{
if ( m_iOldHealth < m_iHealth )
{
m_fLastHealTime = gpGlobals->curtime;
}
UpdateDamageEffects( true );
m_iOldHealth = m_iHealth;
}
bool bNoDraw = IsEffectActive( EF_NODRAW );
if ( bNoDraw != m_bLastNoDraw )
{
m_bLastNoDraw = bNoDraw;
// give weapons a chance to update visibility as I'm hidden/shown
for ( int i = 0; i < WeaponCount(); i++ )
{
C_BaseCombatWeapon *pWeapon = GetWeapon( i );
if ( pWeapon )
{
pWeapon->UpdateVisibility();
}
}
}
UpdateFireEmitters();
}
// note: this function doesn't seem to be used in sp. Maybe just in a netgame?
void C_ASW_Marine::ProcessMuzzleFlashEvent()
{
Warning("[C] marine ProcessMuzzleFlashEvent\n");
C_ASW_Weapon *pWeapon = GetActiveASWWeapon();
if ( !pWeapon )
return;
/*
Vector vector;
QAngle angles;
int iAttachment = LookupAttachment( "muzzle_flash" );
if ( iAttachment >= 0 )
{
bool bFoundAttachment = GetAttachment( iAttachment, vector, angles );
// If we have an attachment, then stick a light on it.
if ( bFoundAttachment )
{
dlight_t *el = effects->CL_AllocDlight( LIGHT_INDEX_MUZZLEFLASH + index );
el->origin = vector;
el->radius = 70;
el->decay = el->radius / 0.05f;
el->die = gpGlobals->curtime + 0.05f;
el->color.r = 255;
el->color.g = 192;
el->color.b = 64;
el->color.exponent = 5;
int shellType = GetShellForAmmoType( pWeapon->GetCSWpnData().szAmmo1 );
QAngle playerAngle = EyeAngles();
Vector vForward, vRight, vUp;
AngleVectors( playerAngle, &vForward, &vRight, &vUp );
QAngle angVelocity;
Vector vVel = vRight * 100 + vUp * 20;
VectorAngles( vVel, angVelocity );
tempents->CSEjectBrass( vector, angVelocity, 120, shellType, this );
}
}
*/
pWeapon->OnMuzzleFlashed();
if (pWeapon->ShouldMarineFlame())
{
m_fFlameTime = gpGlobals->curtime + pWeapon->GetFireRate() + 0.02f;
return;
}
else if (pWeapon->ShouldMarineFireExtinguish())
{
m_fFireExtinguisherTime = gpGlobals->curtime + pWeapon->GetFireRate() + 0.02f;
return;
}
// attach muzzle flash particle system effect
int iAttachment = pWeapon->GetMuzzleAttachment();
if ( iAttachment > 0 )
{
#ifndef _DEBUG
float flScale = pWeapon->GetMuzzleFlashScale();
if (pWeapon->GetMuzzleFlashRed())
{
FX_ASW_RedMuzzleEffectAttached( flScale, pWeapon->GetRefEHandle(), iAttachment, NULL, false );
}
else
{
FX_ASW_MuzzleEffectAttached( flScale, pWeapon->GetRefEHandle(), iAttachment, NULL, false );
}
#endif
}
}
C_ASW_Weapon* C_ASW_Marine::GetActiveASWWeapon( void ) const
{
return static_cast<C_ASW_Weapon*>( GetActiveWeapon() );
}
void C_ASW_Marine::DoAnimationEvent( PlayerAnimEvent_t event )
{
MDLCACHE_CRITICAL_SECTION();
m_PlayerAnimState->DoAnimationEvent( event );
}
void C_ASW_Marine::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options )
{
if ( GetCurrentMeleeAttack() )
{
if ( !GetCurrentMeleeAttack()->AllowNormalAnimEvent( this, event ) )
return;
}
//Msg("event = %d\n", event);
if ( event == AE_ASW_FOOTSTEP || event == AE_MARINE_FOOTSTEP )
{
Vector vel;
EstimateAbsVelocity( vel );
surfacedata_t *pSurface = GetGroundSurface();
//Msg("Footstep t=%f velsize=%f\n", gpGlobals->curtime, vel.Length());
if (pSurface)
{
//ASWGameMovement()->PlantFootprint( pSurface );
MarineFootprintFX( pSurface, vel );
MarineStepSound( pSurface, GetAbsOrigin(), vel );
}
return;
}
else if (event == AE_MARINE_RELOAD_SOUND_A)
{
if (GetActiveASWWeapon())
GetActiveASWWeapon()->ASWReloadSound(0);
}
else if (event == AE_MARINE_RELOAD_SOUND_B)
{
if (GetActiveASWWeapon())
GetActiveASWWeapon()->ASWReloadSound(1);
}
else if (event == AE_MARINE_RELOAD_SOUND_C)
{
if (GetActiveASWWeapon())
GetActiveASWWeapon()->ASWReloadSound(2);
}
else if (event == AE_NPC_BODYDROP_HEAVY)
{
// play a sound of him dropping to the floor
if ( GetFlags() & FL_ONGROUND )
{
EmitSound( "AI_BaseNPC.BodyDrop_Heavy" );
}
return;
}
else if ( event == AE_SCREEN_SHAKE )
{
// TODO: Fix
//ASW_ShakeAnimEvent( this, options );
}
BaseClass::FireEvent(origin, angles, event, options);
}
void C_ASW_Marine::MarineStepSound( surfacedata_t *psurface, const Vector &vecOrigin, const Vector &vecVelocity )
{
int fWalking;
float fvol;
Vector knee;
Vector feet;
float height;
float speed;
float velrun;
float velwalk;
float flduck;
int fLadder;
if ( GetFlags() & (FL_FROZEN|FL_ATCONTROLS))
return;
if ( GetMoveType() == MOVETYPE_NOCLIP || GetMoveType() == MOVETYPE_OBSERVER )
return;
speed = VectorLength( vecVelocity );
float groundspeed = Vector2DLength( vecVelocity.AsVector2D() );
// determine if we are on a ladder
fLadder = ( GetMoveType() == MOVETYPE_LADDER );
// UNDONE: need defined numbers for run, walk, crouch, crouch run velocities!!!!
if ( ( GetFlags() & FL_DUCKING) || fLadder )
{
velwalk = 60; // These constants should be based on cl_movespeedkey * cl_forwardspeed somehow
velrun = 80;
flduck = 100;
}
else
{
velwalk = 90;
velrun = 220;
flduck = 0;
}
bool onground = true; //( GetFlags() & FL_ONGROUND );
bool movingalongground = ( groundspeed > 0.0f );
bool moving_fast_enough = ( speed >= velwalk );
// To hear step sounds you must be either on a ladder or moving along the ground AND
// You must be moving fast enough
//Msg("og=%d ma=%d mf=%d\n", onground, movingalongground, moving_fast_enough);
if ( !moving_fast_enough || !(fLadder || ( onground && movingalongground )) )
return;
// MoveHelper()->PlayerSetAnimation( PLAYER_WALK );
fWalking = speed < velrun;
VectorCopy( vecOrigin, knee );
VectorCopy( vecOrigin, feet );
height = 72.0f; // bad
knee[2] = vecOrigin[2] + 0.2 * height;
// find out what we're stepping in or on...
if ( fLadder )
{
psurface = physprops->GetSurfaceData( physprops->GetSurfaceIndex( "ladder" ) );
fvol = 0.5;
}
else if ( enginetrace->GetPointContents( knee ) & MASK_WATER )
{
static int iSkipStep = 0;
//DoWaterRipples();
if ( iSkipStep == 0 )
{
iSkipStep++;
return;
}
if ( iSkipStep++ == 3 )
{
iSkipStep = 0;
}
psurface = physprops->GetSurfaceData( physprops->GetSurfaceIndex( "wade" ) );
/*
static Vector s_MarineWaterSplashColor( 0.5, 0.5, 0.5 );
trace_t trace;
Vector vecStart = GetAbsOrigin() + Vector(0, 0, 60);
Vector vecEnd = GetAbsOrigin() + Vector(0, 0, 1);;
UTIL_TraceLine(vecStart, vecEnd, MASK_WATER, NULL, COLLISION_GROUP_NONE, &trace);
if( trace.fraction < 1 && !trace.startsolid)
{
FX_WaterRipple(trace.endpos, 1.0f, &s_MarineWaterSplashColor, 1.5f, 1.0f);
}
*/
fvol = 0.65;
}
else if ( enginetrace->GetPointContents( feet ) & MASK_WATER )
{
psurface = physprops->GetSurfaceData( physprops->GetSurfaceIndex( "water" ) );
fvol = fWalking ? 0.2 : 0.5;
}
else
{
if ( !psurface )
return;
switch ( psurface->game.material )
{
default:
case CHAR_TEX_CONCRETE:
fvol = fWalking ? 0.2 : 0.5;
break;
case CHAR_TEX_METAL:
fvol = fWalking ? 0.4 : 1.0;
break;
case CHAR_TEX_DIRT:
fvol = fWalking ? 0.25 : 0.55;
break;
case CHAR_TEX_VENT:
fvol = fWalking ? 0.4 : 0.7;
break;
case CHAR_TEX_GRATE:
fvol = fWalking ? 0.2 : 0.5;
break;
case CHAR_TEX_TILE:
fvol = fWalking ? 0.2 : 0.5;
break;
case CHAR_TEX_SLOSH:
fvol = fWalking ? 0.2 : 0.5;
break;
}
}
// play the sound
// 65% volume if ducking
if ( GetFlags() & FL_DUCKING )
{
fvol *= 0.65;
}
PlayStepSound( feet, psurface, fvol, false );
}
const char *C_ASW_Marine::GetMarineFootprintParticleName( surfacedata_t *psurface )
{
switch ( psurface->game.material )
{
case CHAR_TEX_SNOW: return "footstep_snow"; break;
}
return "footstep_snow";
}
#define PLAYER_HALFWIDTH 12
void C_ASW_Marine::MarineFootprintFX( surfacedata_t *psurface, const Vector &vecVelocity )
{
// note, velocity is not used yet, will use it to determine how big of an effect the footprint will have eventually
if ( !psurface )
return;
bool bPlantFootprint = false;
switch ( psurface->game.material )
{
case CHAR_TEX_SNOW: bPlantFootprint = true; break;
}
if ( bPlantFootprint )
{
Vector right;
AngleVectors( GetAbsAngles(), 0, &right, 0 );
// Figure out where the top of the stepping leg is
trace_t tr;
Vector hipOrigin;
VectorMA( GetAbsOrigin(),
m_bIsFootprintOnLeft ? -PLAYER_HALFWIDTH : PLAYER_HALFWIDTH,
right, hipOrigin );
// Find where that leg hits the ground
UTIL_TraceLine( hipOrigin, hipOrigin + Vector(0, 0, -COORD_EXTENT * 1.74),
MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr);
// Splat a decal
//CPVSFilter filter( tr.endpos );
//te->FootprintDecal( filter, 0.0f, &tr.endpos, &right, tr.GetEntityIndex(),
// 10, marine->m_chTextureType ); //gDecals[footprintDecal].index
DispatchParticleEffect( GetMarineFootprintParticleName( psurface ), tr.endpos, GetAbsAngles() );
}
// Switch feet for next time
m_bIsFootprintOnLeft = !m_bIsFootprintOnLeft;
}
surfacedata_t* C_ASW_Marine::GetGroundSurface()
{
//
// Find the name of the material that lies beneath the player.
//
Vector start, end;
VectorCopy( GetAbsOrigin() + Vector(0,0,1), start );
VectorCopy( start, end );
// Straight down
end.z -= 38; // was 64
// Fill in default values, just in case.
Ray_t ray;
ray.Init( start, end, GetCollideable()->OBBMins(), GetCollideable()->OBBMaxs() );
trace_t trace;
UTIL_TraceRay( ray, MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace );
if ( trace.fraction == 1.0f )
return NULL; // no ground
return physprops->GetSurfaceData( trace.surface.surfaceProps );
}
void C_ASW_Marine::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force )
{
if ( !psurface )
return;
unsigned short stepSoundName = m_bStepSideLeft ? psurface->sounds.runStepLeft : psurface->sounds.runStepRight;
m_bStepSideLeft = !m_bStepSideLeft;
if ( !stepSoundName )
return;
IPhysicsSurfaceProps *physprops = MoveHelper( )->GetSurfaceProps();
const char *pSoundName = physprops->GetString( stepSoundName );
CSoundParameters params;
if ( !CBaseEntity::GetParametersForSound( pSoundName, params, NULL ) )
return;
CLocalPlayerFilter filter;
EmitSound_t ep;
ep.m_nChannel = CHAN_BODY;
ep.m_pSoundName = params.soundname;
ep.m_flVolume = ( asw_override_footstep_volume.GetBool() ) ? fvol : params.volume;
ep.m_SoundLevel = params.soundlevel;
ep.m_nFlags = 0;
ep.m_nPitch = params.pitch;
ep.m_pOrigin = &vecOrigin;
EmitSound( filter, entindex(), ep );
}
bool C_ASW_Marine::IsAnimatingReload()
{
return m_PlayerAnimState->IsAnimatingReload();
}
bool C_ASW_Marine::IsDoingEmoteGesture()
{
return m_PlayerAnimState->IsDoingEmoteGesture();
}
void C_ASW_Marine::PostThink()
{
}
//-----------------------------------------------------------------------------
// Purpose: Creates, destroys, and updates the flashlight effect as needed.
//-----------------------------------------------------------------------------
void C_ASW_Marine::UpdateFlashlight()
{
// The dim light is the flashlight.
#if ASW_PROJECTOR_FLASHLIGHT
if ( IsEffectActive( EF_DIMLIGHT ) )
{
if ( !m_pFlashlight )
{
// Turned on the headlight; create it.
m_pFlashlight = new CFlashlightEffect(index);
if ( !m_pFlashlight )
return;
m_pFlashlight->TurnOn();
}
#ifdef ASW_FLASHLIGHT_DLIGHT
if ( !m_pFlashlightDLight || (m_pFlashlightDLight->key != index) )
{
m_pFlashlightDLight = effects->CL_AllocDlight ( index );
}
#endif
//m_fAmbientLight = asw_flashlight_marine_ambient.GetFloat();
//m_fLightingScale = asw_flashlight_marine_lightscale.GetFloat();
Vector vecForward, vecRight, vecUp;
if (m_pFlashlightDLight)
{
AngleVectors( GetLocalAngles(), &vecForward, &vecRight, &vecUp );
m_pFlashlightDLight->origin = GetAbsOrigin() + vecForward * asw_flashlight_dlight_offsetx.GetFloat()
+ vecRight * asw_flashlight_dlight_offsety.GetFloat() + vecUp * asw_flashlight_dlight_offsetz.GetFloat();
m_pFlashlightDLight->color.r = asw_flashlight_dlight_r.GetInt();
m_pFlashlightDLight->color.g = asw_flashlight_dlight_g.GetInt();
m_pFlashlightDLight->color.b = asw_flashlight_dlight_b.GetInt();
m_pFlashlightDLight->radius = asw_flashlight_dlight_radius.GetFloat();
//m_pFlashlightDLight->color.exponent = 5;
//m_pFlashlightDLight->decay = 0;
m_pFlashlightDLight->die = gpGlobals->curtime + 30.0f;
}
C_ASW_Player *pPlayer = GetCommander();
QAngle angFlashlight;
if ( pPlayer && C_BasePlayer::IsLocalPlayer(pPlayer) && pPlayer->GetMarine() == this && GetHealth() > 0 )
{
angFlashlight = pPlayer->EyeAnglesWithCursorRoll(); // z component of eye angles holds the distance of the cursor from the marine
angFlashlight.x = angFlashlight.z;
angFlashlight.z = 0;
if (angFlashlight.x > 42)
angFlashlight.x = 42;
}
else
{
angFlashlight = ASWEyeAngles();
angFlashlight.x = 20; // tilt the flashlight down by default
}
AngleVectors( angFlashlight, &vecForward, &vecRight, &vecUp );
// Update the light with the new position and direction.
// asw fixme: change FLASHLIGHT_DISTANCE to a trace out ahead?
// hm, distance is only used for the old flashlight (which was just a dlight?)
Vector vecFlashLightPos = EyePosition() + vecForward * 30.0f;
// projector light:
m_pFlashlight->UpdateLight( entindex(), vecFlashLightPos, vecForward, vecRight, vecUp, 0.0f, 0.0f, 0.0f, false, NULL );
}
else
{
if (m_pFlashlight)
{
// Turned off the flashlight; delete it.
m_pFlashlight->TurnOff();
delete m_pFlashlight;
m_pFlashlight = NULL;
}
if (m_pFlashlightDLight)
{
m_pFlashlightDLight->die = gpGlobals->curtime + 0.001;
m_pFlashlightDLight = NULL;
}
//m_fAmbientLight = asw_marine_ambient.GetFloat();
//m_fLightingScale = asw_marine_lightscale.GetFloat();
}
#else
// beam flashlight
if ( IsEffectActive( EF_DIMLIGHT ) )
{
// get eye angles
Vector vecForward, vecRight, vecUp;
QAngle angFlashlight;
/*C_ASW_Player *pPlayer = ToASW_Player( C_BasePlayer::GetLocalPlayer() );
if ( pPlayer && pPlayer->GetMarine() == this && GetHealth() > 0 )
{
angFlashlight = pPlayer->EyeAnglesWithCursorRoll(); // z component of eye angles holds the distance of the cursor from the marine
angFlashlight.x = angFlashlight.z;
angFlashlight.z = 0;
if (angFlashlight.x > 42)
angFlashlight.x = 42;
}
else*/
{
angFlashlight = ASWEyeAngles();
//angFlashlight.x = 20; // tilt the flashlight down by default
}
AngleVectors( angFlashlight, &vecForward, &vecRight, &vecUp );
int iAttachment = LookupAttachment( "muzzle_flash" );
if ( iAttachment < 0 )
return;
Vector vecOrigin;
QAngle dummy;
GetAttachment( iAttachment, vecOrigin, dummy );
vecOrigin = EyePosition();
trace_t tr;
UTIL_TraceLine( vecOrigin, vecOrigin + (vecForward * 200), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
if( !m_pFlashlightBeam )
{
BeamInfo_t beamInfo;
beamInfo.m_nType = TE_BEAMPOINTS;
beamInfo.m_vecStart = tr.startpos;
beamInfo.m_vecEnd = tr.endpos;
beamInfo.m_pszModelName = "sprites/glow01.vmt";
beamInfo.m_pszHaloName = "sprites/glow01.vmt";
beamInfo.m_flHaloScale = 3.0;
beamInfo.m_flWidth = 8.0f;
beamInfo.m_flEndWidth = 55.0f;
beamInfo.m_flFadeLength = 300.0f;
beamInfo.m_flAmplitude = 0;
beamInfo.m_flBrightness = 60.0;
beamInfo.m_flSpeed = 0.0f;
beamInfo.m_nStartFrame = 0.0;
beamInfo.m_flFrameRate = 0.0;
beamInfo.m_flRed = 255.0;
beamInfo.m_flGreen = 255.0;
beamInfo.m_flBlue = 255.0;
beamInfo.m_nSegments = 8;
beamInfo.m_bRenderable = true;
beamInfo.m_flLife = 0.5;
beamInfo.m_nFlags = FBEAM_FOREVER | FBEAM_ONLYNOISEONCE | FBEAM_NOTILE | FBEAM_HALOBEAM;
m_pFlashlightBeam = beams->CreateBeamPoints( beamInfo );
}
if( m_pFlashlightBeam )
{
BeamInfo_t beamInfo;
beamInfo.m_vecStart = tr.startpos;
beamInfo.m_vecEnd = tr.endpos;
beamInfo.m_flRed = 255.0;
beamInfo.m_flGreen = 255.0;
beamInfo.m_flBlue = 255.0;
beams->UpdateBeamInfo( m_pFlashlightBeam, beamInfo );
//dlight_t *el = effects->CL_AllocDlight( 0 );
//el->origin = tr.endpos;
//el->radius = 50;
//el->color.r = 200;
//el->color.g = 200;
//el->color.b = 200;
//el->die = gpGlobals->curtime + 0.1;
}
}
else if ( m_pFlashlightBeam )
{
ReleaseFlashlightBeam();
}
#endif
}
bool C_ASW_Marine::CreateLightEffects( void )
{
// we ignore the normal dlight created for the EF_ flags
// as we use proper flashlights
return false;
}
bool C_ASW_Marine::Simulate()
{
UpdateFlashlight();
BaseClass::Simulate();
return true;
}
void C_ASW_Marine::EstimateAbsVelocity( Vector& vel )
{
if (ShouldPredict())
{
vel = GetAbsVelocity();
return;
}
BaseClass::EstimateAbsVelocity(vel);
}
void C_ASW_Marine::StartFlamerLoop()
{
if ( m_pFlamerLoopSound )
return;
CPASAttenuationFilter filter( this );
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
m_pFlamerLoopSound = controller.SoundCreate( filter, entindex(), "ASW_Weapon_Flamer.FlameLoop" );
CSoundEnvelopeController::GetController().Play( m_pFlamerLoopSound, 1.0, 100 );
//CSoundEnvelopeController::GetController().SoundChangeVolume( m_pEngineSound1, 0.7, 2.0 );
}
void C_ASW_Marine::StopFlamerLoop()
{
if ( m_pFlamerLoopSound )
{
//Msg("Ending flamer loop!\n");
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
controller.SoundDestroy( m_pFlamerLoopSound );
m_pFlamerLoopSound = NULL;
EmitSound("ASW_Weapon_Flamer.FlameStop");
}
}
void C_ASW_Marine::StartFireExtinguisherLoop()
{
if ( m_pFireExtinguisherLoopSound )
return;
CPASAttenuationFilter filter( this );
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
m_pFireExtinguisherLoopSound = controller.SoundCreate( filter, entindex(), "ASW_Extinguisher.OnLoop" );
CSoundEnvelopeController::GetController().Play( m_pFireExtinguisherLoopSound, 1.0, 100 );
}
void C_ASW_Marine::StopFireExtinguisherLoop()
{
if ( m_pFireExtinguisherLoopSound )
{
//Msg("Ending fire extinguisher loop!\n");
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
controller.SoundDestroy( m_pFireExtinguisherLoopSound );
m_pFireExtinguisherLoopSound = NULL;
EmitSound( "ASW_Extinguisher.Stop" );
}
}
void C_ASW_Marine::DoMuzzleFlash()
{
return; // asw - muzzle flashes are triggered by tracer usermessages instead to save bandwidth
// Our weapon takes our muzzle flash command
C_BaseCombatWeapon *pWeapon = GetActiveWeapon();
if ( pWeapon )
{
pWeapon->DoMuzzleFlash();
//NOTENOTE: We do not chain to the base here
}
else
{
BaseClass::DoMuzzleFlash();
}
}
void C_ASW_Marine::CreateSentryBuildDisplay()
{
if ( m_hSentryBuildDisplay )
{
ParticleProp()->StopEmissionAndDestroyImmediately( m_hSentryBuildDisplay );
}
m_hSentryBuildDisplay = ParticleProp()->Create( "sentry_build_display", PATTACH_ABSORIGIN_FOLLOW, -1, Vector( 0, 0, 8 ) );
SetSentryBuildDisplayEnabled( true );
}
void C_ASW_Marine::SetSentryBuildDisplayEnabled( bool state )
{
if ( !m_hSentryBuildDisplay )
CreateSentryBuildDisplay();
if ( m_hSentryBuildDisplay )
{
if ( state == true )
m_hSentryBuildDisplay->SetControlPoint( 5, Vector( 10, 124, 203 ) );
else
m_hSentryBuildDisplay->SetControlPoint( 5, Vector( 240, 40, 40 ) );
}
}
void C_ASW_Marine::DestroySentryBuildDisplay()
{
if ( m_hSentryBuildDisplay )
{
ParticleProp()->StopEmissionAndDestroyImmediately( m_hSentryBuildDisplay );
m_hSentryBuildDisplay = NULL;
}
}
void C_ASW_Marine::UpdateDamageEffects( bool bHealthChanged )
{
//bool bHealthChanged = m_iLastHealth != GetHealth();
//if ( bHealthChanged )
// m_iLastHealth = GetHealth();
float flHealthFraction = HealthFraction();
if ( flHealthFraction < 0.35f && !m_bKnockedOut )
{
if ( bHealthChanged )
{
// Critical health
if ( !m_hCriticalHeathEffect )
{
m_hCriticalHeathEffect = ParticleProp()->Create( "criticalHealth", PATTACH_ABSORIGIN_FOLLOW, -1, Vector( 0, 0, 50 ) );
}
if ( m_hLowHeathEffect )
{
m_hLowHeathEffect->StopEmission(false, false, true);
m_hLowHeathEffect = NULL;
}
}
if ( m_flNextDamageSparkTime < gpGlobals->curtime )
{
// spark at slightly random intervals
float flSparkRate = asw_damage_spark_rate.GetFloat();
EmitSound( "SuitDamage.Sparks" );
ParticleProp()->Create( "sparks_heavy_master", PATTACH_ABSORIGIN_FOLLOW, -1, Vector( 0, 0, 50 ) );
m_flNextDamageSparkTime = gpGlobals->curtime + flSparkRate + flSparkRate * RandomFloat( 0.0f, 2.0f );
}
}
else if ( flHealthFraction < 0.65f && !m_bKnockedOut )
{
if ( bHealthChanged )
{
// Low health
if ( !m_hLowHeathEffect )
{
m_hLowHeathEffect = ParticleProp()->Create( "lowHealth", PATTACH_ABSORIGIN_FOLLOW, -1, Vector( 0, 0, 50 ) );
}
if ( m_hCriticalHeathEffect )
{
m_hCriticalHeathEffect->StopEmission(false, false , true);
m_hCriticalHeathEffect = NULL;
}
}
if ( m_flNextDamageSparkTime < gpGlobals->curtime )
{
// spark at slightly random intervals
float flSparkRate = asw_damage_spark_rate.GetFloat() * 3.0f;
EmitSound( "SuitDamage.Sparks" );
ParticleProp()->Create( "sparks_light_master", PATTACH_ABSORIGIN_FOLLOW, -1, Vector( 0, 0, 50 ) );
m_flNextDamageSparkTime = gpGlobals->curtime + flSparkRate + flSparkRate * RandomFloat( 0.0f, 2.0f );
}
}
else
{
// Safe health
if ( m_hLowHeathEffect )
{
m_hLowHeathEffect->StopEmission(false, false, true);
m_hLowHeathEffect = NULL;
}
if ( m_hCriticalHeathEffect )
{
m_hCriticalHeathEffect->StopEmission(false, false, true);
m_hCriticalHeathEffect = NULL;
}
}
}
void C_ASW_Marine::DoImpactEffect( trace_t &tr, int nDamageType )
{
return;
}
void C_ASW_Marine::UpdateHeartbeat()
{
if ( !ShouldPredict() || !GetMarineResource() )
return;
if (GetHealth() > 0 && GetMarineResource()->GetHealthPercent() < 0.6f)
{
if (!ASWGameRules() || ASWGameRules()->GetGameState() != ASW_GS_INGAME)
return;
if (gpGlobals->curtime > m_fNextHeartbeat)
{
float fHeartVolume = GetMarineResource()->GetHealthPercent() / 0.6f;
fHeartVolume = (1.0f - fHeartVolume); // * 3.0f;
CLocalPlayerFilter filter;
EmitSound_t ep;
ep.m_nChannel = CHAN_AUTO; // CHAN_STATIC ?
ep.m_pSoundName = "Misc.Heartbeat";
ep.m_flVolume = fHeartVolume;
//ep.m_nPitch = PITCH_NORM;
ep.m_SoundLevel = SNDLVL_NORM;
//ep.m_pOrigin = &GetAbsOrigin();
//Msg("heart volume = %f\n", fHeartVolume);
//while (ep.m_flVolume > 0)
//{
//EmitSound(filter, entindex(), ep);
//ep.m_flVolume -= 1.0f;
//}
C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, ep );
// 1.2 when on 50 health
// 0.8 when on 0
float fHeartDelay = 0.8 + (GetMarineResource()->GetHealthPercent() / 0.6f) * 0.4;
m_fNextHeartbeat = gpGlobals->curtime + fHeartDelay;
}
}
}
const Vector& C_ASW_Marine::GetFacingPoint()
{
if (m_vecFacingPointFromServer != vec3_origin)
{
return m_vecFacingPointFromServer;
}
return m_vecFacingPoint;
}
IASW_Client_Vehicle* C_ASW_Marine::GetASWVehicle()
{
return dynamic_cast<IASW_Client_Vehicle*>(m_hASWVehicle.Get());
}
// player has the mouse over something, check if we want to offer an interaction with that
void C_ASW_Marine::MouseOverEntity( C_BaseEntity* pEnt, Vector vecCrosshairAimingPos )
{
if (!GetCommander() || !IsInhabited())
return;
if (!IsAlive())
return;
C_ASW_Weapon* pWeapon = GetActiveASWWeapon();
if (pWeapon)
{
pWeapon->MouseOverEntity( pEnt, vecCrosshairAimingPos );
}
// asw temp
//ASWInput()->SetHighlightEntity(pEnt);
}
void C_ASW_Marine::NotifyShouldTransmit( ShouldTransmitState_t state )
{
// Remove all addon models if we go out of the PVS.
if ( state == SHOULDTRANSMIT_END )
{
if( m_pFlashlightBeam != NULL )
{
ReleaseFlashlightBeam();
}
}
BaseClass::NotifyShouldTransmit( state );
}
void C_ASW_Marine::ReleaseFlashlightBeam( void )
{
if( m_pFlashlightBeam )
{
m_pFlashlightBeam->flags = 0;
m_pFlashlightBeam->die = gpGlobals->curtime - 1;
m_pFlashlightBeam = NULL;
}
}
// EF_NODRAW isn't preventing the marine from being drawn, strangely. So we do a visible check here before drawing.
int C_ASW_Marine::DrawModel( int flags, const RenderableInstance_t &instance )
{
if (!IsVisible())
return 0;
int iResult = BaseClass::DrawModel(flags, instance);
m_vecLastRenderedPos = GetRenderOrigin();
return iResult;
}
void C_ASW_Marine::SetPoisoned(float f)
{
m_fPoison = f;
}
// IK the left hand?
bool FindWeaponAttachmentBone( C_BaseCombatWeapon *pWeapon, int &iWeaponBone )
{
if ( !pWeapon )
return false;
CStudioHdr *pHdr = pWeapon->GetModelPtr();
if ( !pHdr )
return false;
for ( iWeaponBone=0; iWeaponBone < pHdr->numbones(); iWeaponBone++ )
{
if ( stricmp( pHdr->pBone( iWeaponBone )->pszName(), "ValveBiped.Bip01_R_Hand" ) == 0 ) //L_Hand_Attach
break;
}
return iWeaponBone != pHdr->numbones();
}
bool FindLeftHandAttachment( C_BaseCombatWeapon *pWeapon, Vector &vecHandPos, QAngle &angHandFacing )
{
if (!pWeapon)
return false;
int iAttachment = pWeapon->LookupAttachment("L_Hand_Attach");
if (iAttachment == -1)
return false;
return pWeapon->GetAttachment( iAttachment, vecHandPos, angHandFacing ); //GetAttachment( "L_Hand_Attach", vecHandPos, angHandFacing );
}
bool FindMyAttachmentBone( C_BaseAnimating *pModel, int &iBone, CStudioHdr *pHdr )
{
if ( !pHdr )
return false;
for ( iBone=0; iBone < pHdr->numbones(); iBone++ )
{
if ( stricmp( pHdr->pBone( iBone )->pszName(), "Valvebiped.Bip01_L_Hand" ) == 0 )
break;
}
return iBone != pHdr->numbones();
}
inline bool IsBoneChildOf( CStudioHdr *pHdr, int iBone, int iParent )
{
if ( iBone == iParent )
return false;
while ( iBone != -1 )
{
if ( iBone == iParent )
return true;
iBone = pHdr->pBone( iBone )->parent;
}
return false;
}
void ApplyDifferenceTransformToChildren(
C_BaseAnimating *pModel,
const matrix3x4_t &mSource,
const matrix3x4_t &mDest,
int iParentBone )
{
CStudioHdr *pHdr = pModel->GetModelPtr();
if ( !pHdr )
return;
// Build a matrix to go from mOriginalHand to mHand.
// ( mDest * Inverse( mSource ) ) * mSource = mDest
matrix3x4_t mSourceInverse, mToDest;
MatrixInvert( mSource, mSourceInverse );
ConcatTransforms( mDest, mSourceInverse, mToDest );
// Now multiply iMyBone and all its children by mToWeaponBone.
for ( int i=0; i < pHdr->numbones(); i++ )
{
if ( IsBoneChildOf( pHdr, i, iParentBone ) )
{
matrix3x4_t &mCur = pModel->GetBoneForWrite( i );
matrix3x4_t mNew;
ConcatTransforms( mToDest, mCur, mNew );
mCur = mNew;
}
}
}
void GetCorrectionMatrices(
const matrix3x4_t &mShoulder,
const matrix3x4_t &mElbow,
const matrix3x4_t &mHand,
matrix3x4_t &mShoulderCorrection,
matrix3x4_t &mElbowCorrection
)
{
// Get the positions of each node so we can get the direction vectors.
Vector vShoulder, vElbow, vHand;
MatrixPosition( mShoulder, vShoulder );
MatrixPosition( mElbow, vElbow );
MatrixPosition( mHand, vHand );
// Get rid of the translation.
matrix3x4_t mOriginalShoulder = mShoulder;
matrix3x4_t mOriginalElbow = mElbow;
MatrixSetColumn( Vector( 0, 0, 0 ), 3, mOriginalShoulder );
MatrixSetColumn( Vector( 0, 0, 0 ), 3, mOriginalElbow );
// Let the IK code align them like it would if we did IK on the joint.
matrix3x4a_t mAlignedShoulder, mAlignedElbow;
mAlignedShoulder = mOriginalShoulder;
mAlignedElbow = mOriginalElbow;
Studio_AlignIKMatrix( mAlignedShoulder, vElbow-vShoulder );
Studio_AlignIKMatrix( mAlignedElbow, vHand-vElbow );
// Figure out the transformation from the aligned bones to the original ones.
matrix3x4a_t mInvAlignedShoulder, mInvAlignedElbow;
MatrixInvert( mAlignedShoulder, mInvAlignedShoulder );
MatrixInvert( mAlignedElbow, mInvAlignedElbow );
ConcatTransforms( mInvAlignedShoulder, mOriginalShoulder, mShoulderCorrection );
ConcatTransforms( mInvAlignedElbow, mOriginalElbow, mElbowCorrection );
}
void C_ASW_Marine::BuildTransformations( CStudioHdr *pHdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed )
{
// First, setup our model's transformations like normal.
BaseClass::BuildTransformations( pHdr, pos, q, cameraTransform, boneMask, boneComputed );
if ( !asw_left_hand_ik.GetInt() )
return;
// If our current weapon has a bone named L_Hand_Attach, then we attach the player's
// left hand (Valvebiped.Bip01_L_Hand) to it.
C_BaseCombatWeapon *pWeapon = GetActiveWeapon();
if ( !pWeapon )
return;
// Have the weapon setup its bones.
pWeapon->SetupBones( NULL, 0, BONE_USED_BY_ANYTHING, gpGlobals->curtime );
int iWeaponBone = 0;
Vector vHandTarget, vecHandFacing;
QAngle angHandFacing;
if ( FindWeaponAttachmentBone( pWeapon, iWeaponBone ) )
//if ( FindLeftHandAttachment( pWeapon, vHandTarget, angHandFacing ) )
{
int iMyBone = 0;
if ( FindMyAttachmentBone( this, iMyBone, pHdr ) )
{
int iHand = iMyBone;
int iElbow = pHdr->pBone( iHand )->parent;
int iShoulder = pHdr->pBone( iElbow )->parent;
matrix3x4a_t *pBones = GetBoneArrayForWrite( );
// Store off the original hand position.
matrix3x4_t mSource = pBones[iHand];
// Figure out the rotation offset from the current shoulder and elbow bone rotations
// and what the IK code's alignment code is going to produce, because we'll have to
// re-apply that offset after the IK runs.
matrix3x4_t mShoulderCorrection, mElbowCorrection;
GetCorrectionMatrices( pBones[iShoulder], pBones[iElbow], pBones[iHand], mShoulderCorrection, mElbowCorrection );
// Do the IK solution.
//Vector vHandTarget;
MatrixPosition( pWeapon->GetBone( iWeaponBone ), vHandTarget );
MatrixAngles( pWeapon->GetBone( iWeaponBone ), angHandFacing );
AngleVectors( angHandFacing, &vecHandFacing );
vHandTarget += vecHandFacing * 15;
Studio_SolveIK( iShoulder, iElbow, iHand, vHandTarget, pBones );
// Now reapply the rotation correction.
matrix3x4_t mTempShoulder = pBones[iShoulder];
matrix3x4_t mTempElbow = pBones[iElbow];
ConcatTransforms( mTempShoulder, mShoulderCorrection, pBones[iShoulder] );
ConcatTransforms( mTempElbow, mElbowCorrection, pBones[iElbow] );
// Now apply the transformation on the hand to the fingers.
matrix3x4_t &mDest = GetBoneForWrite( iHand );
ApplyDifferenceTransformToChildren( this, mSource, mDest, iHand );
}
}
}
void C_ASW_Marine::UpdateFireEmitters()
{
bool bOnFire = (m_bOnFire && !IsEffectActive(EF_NODRAW));
if (bOnFire != m_bClientOnFire)
{
m_bClientOnFire = bOnFire;
if (m_bClientOnFire)
{
if ( !m_pBurningEffect )
{
m_pBurningEffect = UTIL_ASW_CreateFireEffect( this );
}
EmitSound( "ASWFire.BurningFlesh" );
}
else
{
if ( m_pBurningEffect )
{
ParticleProp()->StopEmission( m_pBurningEffect );
m_pBurningEffect = NULL;
}
StopSound("ASWFire.BurningFlesh");
if ( C_BaseEntity::IsAbsQueriesValid() )
EmitSound("ASWFire.StopBurning");
}
}
}
void C_ASW_Marine::UpdateOnRemove()
{
BaseClass::UpdateOnRemove();
m_bOnFire = false;
UpdateFireEmitters();
if ( m_pHealEmitter )
{
ParticleProp()->StopEmission( m_pHealEmitter );
m_pHealEmitter = NULL;
}
// flamethrower handles its own effects now
/*
if ( m_hFlameEmitter.IsValid() )
{
m_hFlameEmitter->SetActive(false);
}
if ( m_hFlameStreamEmitter.IsValid() )
{
m_hFlameStreamEmitter->SetActive(false);
}
*/
// extinguisher weapons handles its own effects now
/*
if ( m_hFireExtinguisherEmitter.IsValid() )
{
m_hFireExtinguisherEmitter->SetActive(false);
}
*/
if (m_hShoulderCone.Get())
{
UTIL_Remove( m_hShoulderCone );
m_hShoulderCone = NULL;
}
}
// helper for movement code which will disable movement in controller mode if you're interacting and need those directionals
bool C_ASW_Marine::IsUsingComputerOrButtonPanel()
{
return m_hUsingEntity.Get() && (
dynamic_cast<C_ASW_Button_Area*>(m_hUsingEntity.Get()) ||
(
dynamic_cast<C_ASW_Computer_Area*>(m_hUsingEntity.Get()) &&
!IsControllingTurret()
)
);
}
void C_ASW_Marine::CreateShoulderCone()
{
if (asw_marine_shoulderlight.GetInt()!=1)
return;
int iAttachment = LookupAttachment( "shoulderlight" );
if ( iAttachment <= 0 )
{
Msg("error, couldn't find shoulderlight attachment for marine\n");
return;
}
C_BaseAnimating *pEnt = new C_BaseAnimating;
if (!pEnt)
{
Msg("Error, couldn't create new C_BaseAnimating\n");
return;
}
if (!pEnt->InitializeAsClientEntity( "models/swarm/shouldercone/shouldercone.mdl", false ))
{
Msg("Error, couldn't InitializeAsClientEntity\n");
pEnt->Release();
return;
}
pEnt->SetParent( this, iAttachment );
pEnt->SetLocalOrigin( Vector( 0, 0, 0 ) );
pEnt->SetLocalAngles( QAngle( 0, 0, 0 ) );
pEnt->SetSolid( SOLID_NONE );
pEnt->RemoveEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID );
m_hShoulderCone = pEnt;
}
// when marine's health falls below this, name starts to blink red
#define MARINE_NAME_PULSE_THRESHOLD 0.5f
void C_ASW_Marine::TickRedName(float delta)
{
if (!GetMarineResource())
return;
// deltatime should be normal regardless of slowmo
float fTimeScale = GameTimescale()->GetCurrentTimescale();
delta *= ( 1.0f / fTimeScale );
float fHealth = GetMarineResource()->GetHealthPercent();
if ( fHealth > MARINE_NAME_PULSE_THRESHOLD || fHealth <= 0 )
{
m_fRedNamePulse -= delta * 2; // take 0.5 seconds to fade completely to normal
}
else
{
float drate = ((MARINE_NAME_PULSE_THRESHOLD - fHealth) / 0.1f) + 2.0f;
if (m_bRedNamePulseUp)
{
m_fRedNamePulse += drate * delta * 0.5f;
}
else
{
m_fRedNamePulse -= drate * delta * 0.5f;
}
// how quick should we pulse? at 60, once per second i.e. 2d
// at 0, 4 times a second? i.e. 8d
}
if (m_fRedNamePulse <= 0)
{
m_fRedNamePulse = 0;
m_bRedNamePulseUp = true;
}
if (m_fRedNamePulse >= 1.0f)
{
m_fRedNamePulse= 1.0f;
m_bRedNamePulseUp = false;
}
}
void C_ASW_Marine::ImpactTrace( trace_t *pTrace, int iDamageType, char *pCustomImpactName )
{
// do nothing
// effects re handled in TraceAttack in the shared file
}
C_BaseAnimating *C_ASW_Marine::BecomeRagdollOnClient()
{
C_BaseAnimating *pRagdoll = BaseClass::BecomeRagdollOnClient();
if ( pRagdoll )
{
pRagdoll->ParticleProp()->Create( "marine_death_ragdoll", PATTACH_ABSORIGIN_FOLLOW );
}
return pRagdoll;
}
C_ASW_Marine* C_ASW_Marine::GetLocalMarine()
{
C_ASW_Player *pPlayer = C_ASW_Player::GetLocalASWPlayer();
if ( !pPlayer )
return NULL;
return pPlayer->GetMarine();
}
void C_ASW_Marine::UpdateElectrifiedArmor()
{
bool bElectrified = IsElectrifiedArmorActive();
if ( bElectrified != m_bClientElectrifiedArmor )
{
bool bLocalPlayer = false;
C_ASW_Player *pPlayer = GetCommander();
C_ASW_Player *pLocalPlayer = C_ASW_Player::GetLocalASWPlayer();
if ( pPlayer == pLocalPlayer && IsInhabited() )
bLocalPlayer = true;
m_bClientElectrifiedArmor = bElectrified;
if ( m_bClientElectrifiedArmor )
{
EmitSound( "ASW_ElectrifiedSuit.TurnOn" );
if ( bLocalPlayer )
EmitSound( "ASW_ElectrifiedSuit.LoopFP" );
else
EmitSound( "ASW_ElectrifiedSuit.Loop" );
if ( !m_pElectrifiedArmorEmitter )
{
m_pElectrifiedArmorEmitter = ParticleProp()->Create( "thorns_marine_buff", PATTACH_ABSORIGIN_FOLLOW, -1, Vector( 0, 0, 50 ) );
}
}
else
{
if ( bLocalPlayer )
{
StopSound( "ASW_ElectrifiedSuit.LoopFP" );
EmitSound( "ASW_ElectrifiedSuit.OffFP" );
}
else
{
StopSound( "ASW_ElectrifiedSuit.Loop" );
}
if ( m_pElectrifiedArmorEmitter )
{
m_pElectrifiedArmorEmitter->StopEmission(false, false, true);
m_pElectrifiedArmorEmitter = NULL;
}
}
}
}
void C_ASW_Marine::UpdateJumpJetEffects()
{
if ( m_iJumpJetting == JJ_JUMP_JETS || m_iJumpJetting == JJ_CHARGE )
{
if ( !m_pJumpJetEffect[0] )
{
const char *pszEffect = "jj_trail_small";
m_pJumpJetEffect[0] = ParticleProp()->Create( pszEffect, PATTACH_POINT_FOLLOW, "jump_jet_l" );
m_pJumpJetEffect[1] = ParticleProp()->Create( pszEffect, PATTACH_POINT_FOLLOW, "jump_jet_r" );
CLocalPlayerFilter filter;
EmitSound( filter, entindex(), "ASW_JumpJet.Activate" );
EmitSound( filter, entindex(), "ASW_JumpJet.Loop" );
}
}
else if ( m_pJumpJetEffect[0] )
{
StopSound( "ASW_JumpJet.Loop" );
ParticleProp()->StopEmission( m_pJumpJetEffect[0] );
m_pJumpJetEffect[0] = NULL;
ParticleProp()->StopEmission( m_pJumpJetEffect[1] );
m_pJumpJetEffect[1] = NULL;
}
}