mirror of
https://github.com/nillerusr/source-engine.git
synced 2025-01-27 15:24:28 +00:00
2452 lines
69 KiB
C++
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;
|
|
}
|
|
} |