diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/dod | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/shared/dod')
79 files changed, 24328 insertions, 0 deletions
diff --git a/game/shared/dod/achievements_dod.cpp b/game/shared/dod/achievements_dod.cpp new file mode 100644 index 0000000..b55b2b4 --- /dev/null +++ b/game/shared/dod/achievements_dod.cpp @@ -0,0 +1,905 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" + +#ifdef CLIENT_DLL + +#include "achievementmgr.h" +#include "baseachievement.h" +#include "c_dod_player.h" +#include "dod_shareddefs.h" +#include "c_dod_objective_resource.h" +#include "dod_gamerules.h" + +CAchievementMgr g_AchievementMgrDOD; // global achievement mgr for DOD + +//----------------------------------------------------------------------------- +// Purpose: Query if the gamerules allows achievement progress at this time +//----------------------------------------------------------------------------- +bool GameRulesAllowsAchievements( void ) +{ + return ( DODGameRules()->State_Get() == STATE_RND_RUNNING ); +} + +class CAchievementDODThrowBackGren : public CBaseAchievement +{ + void Init() + { + // listen for player kill enemy events, base class will increment count each time that happens + SetFlags( ACH_LISTEN_PLAYER_KILL_ENEMY_EVENTS | ACH_SAVE_GLOBAL ); + SetGoal( 1 ); + } + + virtual void Event_EntityKilled( CBaseEntity *pVictim, CBaseEntity *pAttacker, CBaseEntity *pInflictor, IGameEvent *event ) + { + if ( !GameRulesAllowsAchievements() ) + return; + + C_DODPlayer *pLocalPlayer = C_DODPlayer::GetLocalDODPlayer(); + + if ( pAttacker == pLocalPlayer && pVictim->GetTeamNumber() != pAttacker->GetTeamNumber() ) + { + // if we are allies and killed with a german grenade + // or if we are axis and killed with a us grenade + + const char *killedwith = event->GetString( "weapon" ); + int iLocalTeam = pLocalPlayer->GetTeamNumber(); + + if ( ( iLocalTeam == TEAM_ALLIES && ( FStrEq( killedwith, "frag_ger" ) || FStrEq( killedwith, "riflegren_ger" ) ) ) || + ( iLocalTeam == TEAM_AXIS && ( FStrEq( killedwith, "frag_us" ) || FStrEq( killedwith, "riflegren_us" ) ) ) ) + { + // This kill was made with an enemy grenade. + IncrementCount(); + } + } + } +}; +DECLARE_ACHIEVEMENT( CAchievementDODThrowBackGren, ACHIEVEMENT_DOD_THROW_BACK_GREN, "DOD_THROW_BACK_GREN", 1 ); + + +class CAchievementDODConsecutiveHeadshots : public CBaseAchievement +{ + void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 1 ); + + // Handled on server + } +}; +DECLARE_ACHIEVEMENT( CAchievementDODConsecutiveHeadshots, ACHIEVEMENT_DOD_CONSECUTIVE_HEADSHOTS, "DOD_CONSECUTIVE_HEADSHOTS", 1 ); + + +class CAchievementDODMGPositionStreak : public CBaseAchievement +{ + void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 1 ); + + // Handled on server + } +}; +DECLARE_ACHIEVEMENT( CAchievementDODMGPositionStreak, ACHIEVEMENT_DOD_MG_POSITION_STREAK, "DOD_MG_POSITION_STREAK", 1 ); + + +class CAchievementDODWinKnifeFight : public CBaseAchievement +{ + void Init() + { + SetFlags( ACH_LISTEN_PLAYER_KILL_ENEMY_EVENTS | ACH_SAVE_GLOBAL ); + SetGoal( 1 ); + } + + virtual void Event_EntityKilled( CBaseEntity *pVictim, CBaseEntity *pAttacker, CBaseEntity *pInflictor, IGameEvent *event ) + { + if ( !GameRulesAllowsAchievements() ) + return; + + C_DODPlayer *pLocalPlayer = C_DODPlayer::GetLocalDODPlayer(); + + if ( pAttacker == pLocalPlayer && pVictim->GetTeamNumber() != pAttacker->GetTeamNumber() ) + { + const char *killedwith = event->GetString( "weapon" ); + + if ( FStrEq( killedwith, "amerknife" ) || FStrEq( killedwith, "spade" ) ) + { + C_DODPlayer *pDodVictim = ToDODPlayer( pVictim ); + + if ( pDodVictim ) + { + CWeaponDODBase *pWpn = pDodVictim->GetActiveDODWeapon(); + + if ( pWpn && pWpn->GetDODWpnData().m_WeaponType == WPN_TYPE_MELEE ) + { + // Kill was made with a melee weapon, killer had melee weapon out + IncrementCount(); + } + } + } + } + } +}; +DECLARE_ACHIEVEMENT( CAchievementDODWinKnifeFight, ACHIEVEMENT_DOD_WIN_KNIFE_FIGHT, "DOD_WIN_KNIFE_FIGHT", 1 ); + +const char *pszOfficialMaps[] = +{ + "dod_anzio", + "dod_avalanche", + "dod_argentan", + "dod_colmar", + "dod_donner", + "dod_flash", + "dod_jagd", + "dod_kalt", + "dod_palermo" +}; + + +class CAchievementDODCustomMaps : public CBaseAchievement +{ + // Requires a player to kill at least one player on 5 different non-official maps + + void Init() + { + SetFlags( ACH_LISTEN_PLAYER_KILL_ENEMY_EVENTS | ACH_SAVE_GLOBAL ); + SetGoal( 5 ); + SetStoreProgressInSteam( true ); + + m_bCheckedCurrentMap = false; + } + + virtual void ListenForEvents() + { + // hax, this is called from LevelInitPreEntity, init per-level here + m_bCheckedCurrentMap = false; + } + + virtual void Event_EntityKilled( CBaseEntity *pVictim, CBaseEntity *pAttacker, CBaseEntity *pInflictor, IGameEvent *event ) + { + if ( m_bCheckedCurrentMap ) + return; + + // don't store the map name if we're not going to give achievement progress + if ( m_pAchievementMgr->WereCheatsEverOn() ) + return; + + C_DODPlayer *pLocalPlayer = C_DODPlayer::GetLocalDODPlayer(); + + if ( pAttacker == pLocalPlayer && pVictim->GetTeamNumber() != pAttacker->GetTeamNumber() ) + { + char szMap[MAX_PATH]; + Q_FileBase( engine->GetLevelName(), szMap, ARRAYSIZE( szMap ) ); + + if ( !IsOfficialMap( szMap ) && !HasPlayedThisCustomMap() ) + { + IncrementCount(); + + UTIL_IncrementMapKey( "killed_a_player" ); + } + + // stop listening + m_bCheckedCurrentMap = true; + } + } + + bool HasPlayedThisCustomMap( void ) + { + return ( UTIL_GetMapKeyCount( "killed_a_player" ) > 0 ); + } + + bool IsOfficialMap( const char *pszMapName ) + { + bool bFound = false; + + for ( int i=0;i<ARRAYSIZE(pszOfficialMaps);i++ ) + { + if ( FStrEq( pszMapName, pszOfficialMaps[i] ) ) + { + bFound = true; + break; + } + } + + return bFound; + } + + bool m_bCheckedCurrentMap; + +}; +DECLARE_ACHIEVEMENT( CAchievementDODCustomMaps, ACHIEVEMENT_DOD_PLAY_CUSTOM_MAPS, "DOD_PLAY_CUSTOM_MAPS", 1 ); + + +class CAchievementDODKillsWithGrenade : public CBaseAchievement +{ + void Init() + { + SetFlags( ACH_LISTEN_PLAYER_KILL_ENEMY_EVENTS | ACH_SAVE_GLOBAL ); + SetGoal( 1 ); + } + + virtual void ListenForEvents() + { + // hax, this is called from LevelInitPreEntity, init per-level here + m_flLastKillTime = 0; + m_iKillCount = 0; + } + + virtual void Event_EntityKilled( CBaseEntity *pVictim, CBaseEntity *pAttacker, CBaseEntity *pInflictor, IGameEvent *event ) + { + if ( !GameRulesAllowsAchievements() ) + return; + + C_DODPlayer *pLocalPlayer = C_DODPlayer::GetLocalDODPlayer(); + + if ( pAttacker == pLocalPlayer ) + { + // reject non-grenade/non-rocket inflictors + const char *killedwith = event->GetString( "weapon" ); + + if ( Q_strncmp( killedwith, "frag_", 5 ) && + Q_strncmp( killedwith, "riflegren_", 10 ) && + !FStrEq( killedwith, "pschreck" ) && + !FStrEq( killedwith, "bazooka" ) ) + { + m_flLastKillTime = 0; + return; + } + + if ( ( gpGlobals->curtime - m_flLastKillTime ) > 0.25 ) + { + m_iKillCount = 0; + } + + m_iKillCount++; + m_flLastKillTime = gpGlobals->curtime; + + if ( m_iKillCount == 4 ) + { + IncrementCount(); + } + } + } + +private: + float m_flLastKillTime; + int m_iKillCount; +}; +DECLARE_ACHIEVEMENT( CAchievementDODKillsWithGrenade, ACHIEVEMENT_DOD_KILLS_WITH_GRENADE, "DOD_KILLS_WITH_GRENADE", 1 ); + + +class CAchievementDODLongRangeRocket : public CBaseAchievement +{ + void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 1 ); + + // Handled on server + } +}; +DECLARE_ACHIEVEMENT( CAchievementDODLongRangeRocket, ACHIEVEMENT_DOD_LONG_RANGE_ROCKET, "DOD_LONG_RANGE_ROCKET", 1 ); + + +class CAchievementDODEndRoundKills : public CBaseAchievement +{ + void Init() + { + SetFlags( ACH_LISTEN_PLAYER_KILL_ENEMY_EVENTS | ACH_SAVE_GLOBAL ); + SetGoal( 1 ); + + m_iKillCount = 0; + } + + virtual void ListenForEvents() + { + ListenForGameEvent( "player_spawn" ); + } + + // Reset the count when we spawn + void FireGameEvent_Internal( IGameEvent *event ) + { + if ( m_iKillCount > 0 && 0 == Q_strcmp( event->GetName(), "player_spawn" ) && C_BasePlayer::GetLocalPlayer() ) + { + int iUserID = event->GetInt("userid"); + + if ( iUserID == C_BasePlayer::GetLocalPlayer()->GetUserID() ) + { + m_iKillCount = 0; + } + } + } + + // count kills in endround. No requirement that your team must have won the round - grenades count + virtual void Event_EntityKilled( CBaseEntity *pVictim, CBaseEntity *pAttacker, CBaseEntity *pInflictor, IGameEvent *event ) + { + DODRoundState state = DODGameRules()->State_Get(); + + if ( state == STATE_ALLIES_WIN || state == STATE_AXIS_WIN ) + { + Assert( pAttacker == C_BasePlayer::GetLocalPlayer() ); + + if ( pVictim->GetTeamNumber() != pAttacker->GetTeamNumber() ) + { + m_iKillCount++; + + if ( m_iKillCount > 3 ) + { + IncrementCount(); + } + } + } + } + + int m_iKillCount; +}; +DECLARE_ACHIEVEMENT( CAchievementDODEndRoundKills, ACHIEVEMENT_DOD_END_ROUND_KILLS, "DOD_END_ROUND_KILLS", 1 ); + + +class CAchievementDODCapLastFlag : public CBaseAchievement +{ + void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 1 ); + + // handled on server + } +}; +DECLARE_ACHIEVEMENT( CAchievementDODCapLastFlag, ACHIEVEMENT_DOD_CAP_LAST_FLAG, "DOD_CAP_LAST_FLAG", 1 ); + + +class CAchievementDODUseEnemyWeapons : public CBaseAchievement +{ + void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 1 ); + } + + // Handled on server +}; +DECLARE_ACHIEVEMENT( CAchievementDODUseEnemyWeapons, ACHIEVEMENT_DOD_USE_ENEMY_WEAPONS, "DOD_USE_ENEMY_WEAPONS", 1 ); + + +class CAchievementDODKillDominatingMG : public CBaseAchievement +{ + void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 1 ); + + // Handled on server + } +}; +DECLARE_ACHIEVEMENT( CAchievementDODKillDominatingMG, ACHIEVEMENT_DOD_KILL_DOMINATING_MG, "DOD_KILL_DOMINATING_MG", 1 ); + + +class CAchievementDODColmarDefense : public CBaseAchievement +{ + void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 1 ); + + // Handled on server + } +}; +DECLARE_ACHIEVEMENT( CAchievementDODColmarDefense, ACHIEVEMENT_DOD_COLMAR_DEFENSE, "DOD_COLMAR_DEFENSE", 1 ); + + +class CAchievementDODJagdOvertimeCap : public CBaseAchievement +{ + void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 1 ); + + // Handled on server + } +}; +DECLARE_ACHIEVEMENT( CAchievementDODJagdOvertimeCap, ACHIEVEMENT_DOD_JAGD_OVERTIME_CAP, "DOD_JAGD_OVERTIME_CAP", 1 ); + + +class CAchievementDODWeaponMastery : public CBaseAchievement +{ + void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 1 ); + + // Handled on server + } +}; +DECLARE_ACHIEVEMENT( CAchievementDODWeaponMastery, ACHIEVEMENT_DOD_WEAPON_MASTERY, "DOD_WEAPON_MASTERY", 1 ); + + +class CAchievementDODBlockCaptures : public CBaseAchievement +{ + void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 1 ); + } + + virtual void ListenForEvents() + { + ListenForGameEvent( "dod_capture_blocked" ); + } + + // New achievement rule - block a capture that would have lost the game for your team + + void FireGameEvent_Internal( IGameEvent *event ) + { + if ( !GameRulesAllowsAchievements() ) + return; + + Assert( FStrEq( event->GetName(), "dod_capture_blocked" ) ); + + // was a blocked defuse or plant, don't count + if ( event->GetBool("bomb") ) + return; + + if ( !g_pObjectiveResource ) + return; + + C_DODPlayer *pPlayer = C_DODPlayer::GetLocalDODPlayer(); + + if ( pPlayer && pPlayer->entindex() == event->GetInt("blocker") ) + { + int iCP = event->GetInt( "cp" ); + + bool bIsLastOwnedPoint = true; + + int iPlayerTeam = pPlayer->GetTeamNumber(); + + for( int i=0;i<g_pObjectiveResource->GetNumControlPoints();i++ ) + { + // assume we own the one we blocked + if ( i == iCP ) + continue; + + // if we find any other points owned by us that aren't hidden, this wasn't the last point + + if( !g_pObjectiveResource->IsCPVisible(i) ) + continue; + + if ( g_pObjectiveResource->GetOwningTeam(i) == iPlayerTeam ) + { + bIsLastOwnedPoint = false; + break; + } + } + + if ( bIsLastOwnedPoint ) + { + IncrementCount(); + } + } + } +}; +DECLARE_ACHIEVEMENT( CAchievementDODBlockCaptures, ACHIEVEMENT_DOD_BLOCK_CAPTURES, "DOD_BLOCK_CAPTURES", 1 ); + + +// achievements part deux + +// kills as allies +// kills as axis +class CBaseAchievementKillsOnTeam : public CBaseAchievement +{ + void Init() + { + SetFlags( ACH_LISTEN_PLAYER_KILL_ENEMY_EVENTS | ACH_SAVE_GLOBAL ); + SetGoal( 5000 ); + SetStoreProgressInSteam( true ); + } + + virtual void Event_EntityKilled( CBaseEntity *pVictim, CBaseEntity *pAttacker, CBaseEntity *pInflictor, IGameEvent *event ) + { + Assert( pAttacker == C_BasePlayer::GetLocalPlayer() ); + + if ( pVictim->GetTeamNumber() != pAttacker->GetTeamNumber() ) + { + if ( pAttacker->GetTeamNumber() == GetTeam() ) + { + IncrementCount(); + } + } + } + + virtual int GetTeam( void ) = 0; +}; + +class CAchievementKillsAsAllies : public CBaseAchievementKillsOnTeam +{ + virtual int GetTeam( void ) { return TEAM_ALLIES; } +}; +DECLARE_ACHIEVEMENT( CAchievementKillsAsAllies, ACHIEVEMENT_DOD_KILLS_AS_ALLIES, "DOD_KILLS_AS_ALLIES", 1 ); + +class CAchievementKillsAsAxis : public CBaseAchievementKillsOnTeam +{ + virtual int GetTeam( void ) { return TEAM_AXIS; } +}; +DECLARE_ACHIEVEMENT( CAchievementKillsAsAxis, ACHIEVEMENT_DOD_KILLS_AS_AXIS, "DOD_KILLS_AS_AXIS", 1 ); + +// rifleman +// assault +// support +// sniper +// mg +// bazooka +class CBaseAchievementKillsAsClass : public CBaseAchievement +{ + void Init() + { + SetFlags( ACH_LISTEN_PLAYER_KILL_ENEMY_EVENTS | ACH_SAVE_GLOBAL ); + SetGoal( 1000 ); + SetStoreProgressInSteam( true ); + } + + virtual void Event_EntityKilled( CBaseEntity *pVictim, CBaseEntity *pAttacker, CBaseEntity *pInflictor, IGameEvent *event ) + { + Assert( pAttacker == C_BasePlayer::GetLocalPlayer() ); + + if ( pVictim->GetTeamNumber() != pAttacker->GetTeamNumber() ) + { + C_DODPlayer *pDODAttacker = ToDODPlayer( pAttacker ); + + if ( pDODAttacker->m_Shared.PlayerClass() == GetClass() ) + { + IncrementCount(); + } + } + } + + virtual int GetClass( void ) = 0; +}; + +#define DECLARE_KILLS_AS_CLASS_ACHIEVEMENT( classIndex, achievementID ) \ +class CAchievement_##achievementID : public CBaseAchievementKillsAsClass \ +{ \ + virtual int GetClass( void ) { return classIndex; } \ +}; \ +DECLARE_ACHIEVEMENT( CAchievement_##achievementID, ACHIEVEMENT_##achievementID, #achievementID, 1 ) \ + +DECLARE_KILLS_AS_CLASS_ACHIEVEMENT( 0, DOD_KILLS_AS_RIFLEMAN ); +DECLARE_KILLS_AS_CLASS_ACHIEVEMENT( 1, DOD_KILLS_AS_ASSAULT ); +DECLARE_KILLS_AS_CLASS_ACHIEVEMENT( 2, DOD_KILLS_AS_SUPPORT ); +DECLARE_KILLS_AS_CLASS_ACHIEVEMENT( 3, DOD_KILLS_AS_SNIPER ); +DECLARE_KILLS_AS_CLASS_ACHIEVEMENT( 4, DOD_KILLS_AS_MG ); +DECLARE_KILLS_AS_CLASS_ACHIEVEMENT( 5, DOD_KILLS_AS_BAZOOKAGUY ); + +// per weapon +class CBaseAchievementKillsWithWeapon : public CBaseAchievement +{ + virtual void Event_EntityKilled( CBaseEntity *pVictim, CBaseEntity *pAttacker, CBaseEntity *pInflictor, IGameEvent *event ) + { + Assert( pAttacker == C_BasePlayer::GetLocalPlayer() ); + + if ( pVictim->GetTeamNumber() != pAttacker->GetTeamNumber() && event != NULL ) + { + if ( FStrEq( event->GetString( "weapon", "" ), GetWeaponName() ) ) + { + IncrementCount(); + } + } + } + + virtual const char *GetWeaponName( void ) = 0; +}; + +#define DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( weaponName, goalKills, achievementID ) \ +class CAchievement_##achievementID : public CBaseAchievementKillsWithWeapon \ +{ \ + void Init() \ + { \ + SetFlags( ACH_LISTEN_PLAYER_KILL_ENEMY_EVENTS | ACH_SAVE_GLOBAL ); \ + SetGoal( goalKills ); \ + SetStoreProgressInSteam( true ); \ + } \ + \ + virtual const char *GetWeaponName( void ) { return weaponName; } \ +}; \ +DECLARE_ACHIEVEMENT( CAchievement_##achievementID, ACHIEVEMENT_##achievementID, #achievementID, 1 ) \ + +DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( "garand", 500, DOD_KILLS_WITH_GARAND ); +DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( "thompson", 500, DOD_KILLS_WITH_THOMPSON ); +DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( "bar", 500, DOD_KILLS_WITH_BAR ); +DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( "spring", 500, DOD_KILLS_WITH_SPRING ); +DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( "30cal", 500, DOD_KILLS_WITH_30CAL ); +DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( "bazooka", 500, DOD_KILLS_WITH_BAZOOKA ); + +DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( "k98", 500, DOD_KILLS_WITH_K98 ); +DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( "mp40", 500, DOD_KILLS_WITH_MP40 ); +DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( "mp44", 500, DOD_KILLS_WITH_MP44 ); +DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( "k98_scoped", 500, DOD_KILLS_WITH_K98SCOPED ); +DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( "mg42", 500, DOD_KILLS_WITH_MG42 ); +DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( "pschreck", 500, DOD_KILLS_WITH_PSCHRECK ); + +DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( "colt", 150, DOD_KILLS_WITH_COLT ); +DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( "p38", 150, DOD_KILLS_WITH_P38 ); +DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( "c96", 150, DOD_KILLS_WITH_C96 ); +DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( "m1carbine", 150, DOD_KILLS_WITH_M1CARBINE ); + +DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( "amerknife", 150, DOD_KILLS_WITH_AMERKNIFE ); +DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( "spade", 150, DOD_KILLS_WITH_SPADE ); +DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( "punch", 150, DOD_KILLS_WITH_PUNCH ); + +DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( "frag_us", 250, DOD_KILLS_WITH_FRAG_US ); +DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( "frag_ger", 250, DOD_KILLS_WITH_FRAG_GER ); +DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( "riflegren_us", 250, DOD_KILLS_WITH_RIFLEGREN_US ); +DECLARE_KILLS_WITH_WEAPON_ACHIEVEMENT( "riflegren_ger", 250, DOD_KILLS_WITH_RIFLEGREN_GER ); + + +// flag captures +class CAchievementDODFlagCaptureGrind : public CBaseAchievement +{ + void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 100 ); + SetStoreProgressInSteam( true ); + } + + virtual void ListenForEvents() + { + ListenForGameEvent( "dod_point_captured" ); + } + + void FireGameEvent_Internal( IGameEvent *event ) + { + Assert( FStrEq( event->GetName(), "dod_point_captured" ) ); + + if ( event->GetBool( "bomb" ) == true ) + return; + + if ( !GameRulesAllowsAchievements() ) + return; + + C_DODPlayer *pPlayer = C_DODPlayer::GetLocalDODPlayer(); + if ( !pPlayer ) + return; + + int iLocalPlayerIndex = pPlayer->entindex(); + + const char *cappers = event->GetString("cappers"); + + int len = Q_strlen(cappers); + for( int i=0;i<len;i++ ) + { + if ( iLocalPlayerIndex == (int)cappers[i] ) + { + IncrementCount(); + break; + } + } + } +}; +DECLARE_ACHIEVEMENT( CAchievementDODFlagCaptureGrind, ACHIEVEMENT_DOD_CAPTURE_GRIND, "DOD_CAPTURE_GRIND", 1 ); + + +class CAchievementDODBlockCapturesGrind : public CBaseAchievement +{ + void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 100 ); + SetStoreProgressInSteam( true ); + } + + virtual void ListenForEvents() + { + ListenForGameEvent( "dod_capture_blocked" ); + } + + void FireGameEvent_Internal( IGameEvent *event ) + { + if ( !GameRulesAllowsAchievements() ) + return; + + Assert( FStrEq( event->GetName(), "dod_capture_blocked" ) ); + + if ( event->GetBool( "bomb" ) ) + return; + + C_DODPlayer *pPlayer = C_DODPlayer::GetLocalDODPlayer(); + + if ( pPlayer && pPlayer->entindex() == event->GetInt( "blocker" ) ) + { + IncrementCount(); + } + } +}; +DECLARE_ACHIEVEMENT( CAchievementDODBlockCapturesGrind, ACHIEVEMENT_DOD_BLOCK_CAPTURES_GRIND, "DOD_BLOCK_CAPTURES_GRIND", 1 ); + + +class CAchievementDODRoundsWonGrind : public CBaseAchievement +{ + void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 100 ); + SetStoreProgressInSteam( true ); + } + + virtual void ListenForEvents() + { + ListenForGameEvent( "dod_round_win" ); + } + + void FireGameEvent_Internal( IGameEvent *event ) + { + if ( !GameRulesAllowsAchievements() ) + return; + + Assert( FStrEq( event->GetName(), "dod_round_win" ) ); + + C_DODPlayer *pPlayer = C_DODPlayer::GetLocalDODPlayer(); + if ( !pPlayer ) + return; + + if ( event->GetInt( "team" ) == pPlayer->GetTeamNumber() ) + { + IncrementCount(); + } + } +}; +DECLARE_ACHIEVEMENT( CAchievementDODRoundsWonGrind, ACHIEVEMENT_DOD_ROUNDS_WON_GRIND, "DOD_ROUNDS_WON_GRIND", 1 ); + + +class CAchievementDODBombsPlantedGrind : public CBaseAchievement +{ + void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 100 ); + SetStoreProgressInSteam( true ); + } + + virtual void ListenForEvents() + { + ListenForGameEvent( "dod_bomb_planted" ); + } + + void FireGameEvent_Internal( IGameEvent *event ) + { + Assert( FStrEq( event->GetName(), "dod_bomb_planted" ) ); + + C_DODPlayer *pPlayer = C_DODPlayer::GetLocalDODPlayer(); + + if ( pPlayer && pPlayer->GetUserID() == event->GetInt("userid" ) ) + { + IncrementCount(); + } + } +}; +DECLARE_ACHIEVEMENT( CAchievementDODBombsPlantedGrind, ACHIEVEMENT_DOD_BOMBS_PLANTED_GRIND, "DOD_BOMBS_PLANTED_GRIND", 1 ); + + +class CAchievementDODBombsDefusedGrind : public CBaseAchievement +{ + void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 100 ); + SetStoreProgressInSteam( true ); + } + + virtual void ListenForEvents() + { + ListenForGameEvent( "dod_bomb_defused" ); + } + + void FireGameEvent_Internal( IGameEvent *event ) + { + Assert( FStrEq( event->GetName(), "dod_bomb_defused" ) ); + + if ( event->GetInt("userid") == C_BasePlayer::GetLocalPlayer()->GetUserID() ) + { + IncrementCount(); + } + } +}; +DECLARE_ACHIEVEMENT( CAchievementDODBombsDefusedGrind, ACHIEVEMENT_DOD_BOMBS_DEFUSED_GRIND, "DOD_BOMBS_DEFUSED_GRIND", 1 ); + +//---------------------------------------------------------------------------------------------------------------- +class CAchievementDOD_All_Pack_1 : public CAchievement_AchievedCount +{ +public: + DECLARE_CLASS( CAchievementDOD_All_Pack_1, CAchievement_AchievedCount ); + void Init() + { + BaseClass::Init(); + SetAchievementsRequired( 51, 0, 51 ); + } + + // Complete all dod achievements +}; +DECLARE_ACHIEVEMENT( CAchievementDOD_All_Pack_1, ACHIEVEMENT_DOD_ALL_PACK_1, "DOD_ALL_PACK_1", 5 ); + + +// 2011 Summer sale achievement +// Win a round on each of the winter themed maps, dod_kalt and dod_colmar +// player is not required to have been present at the start of the round +class CAchievementDODBeatTheHeat : public CBaseAchievement +{ +public: + DECLARE_CLASS( CAchievementDODBeatTheHeat, CBaseAchievement ); + void Init() + { + SetFlags( ACH_SAVE_GLOBAL | ACH_HAS_COMPONENTS ); + + static const char *szComponents[] = + { + "dod_kalt", "dod_colmar" + }; + m_pszComponentNames = szComponents; + m_iNumComponents = ARRAYSIZE( szComponents ); + SetGoal( m_iNumComponents ); + + BaseClass::Init(); + } + + virtual void ListenForEvents() + { + ListenForGameEvent( "dod_round_win" ); + } + + void FireGameEvent_Internal( IGameEvent *event ) + { + if ( !GameRulesAllowsAchievements() ) + return; + + Assert( FStrEq( event->GetName(), "dod_round_win" ) ); + + C_DODPlayer *pPlayer = C_DODPlayer::GetLocalDODPlayer(); + if ( !pPlayer ) + return; + + if ( event->GetInt( "team" ) == pPlayer->GetTeamNumber() ) + { + OnComponentEvent( m_pAchievementMgr->GetMapName() ); + } + } +}; +DECLARE_ACHIEVEMENT( CAchievementDODBeatTheHeat, ACHIEVEMENT_DOD_BEAT_THE_HEAT, "DOD_BEAT_THE_HEAT", 1 ); + +// Winter 2011 +class CAchievementDODCollectHolidayGifts : public CBaseAchievement +{ + void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 3 ); + SetStoreProgressInSteam( true ); + } + + virtual void ListenForEvents() + { + ListenForGameEvent( "christmas_gift_grab" ); + } + + void FireGameEvent_Internal( IGameEvent *event ) + { + // if ( !UTIL_IsHolidayActive( kHoliday_Christmas ) ) + // return; + + if ( Q_strcmp( event->GetName(), "christmas_gift_grab" ) == 0 ) + { + int iPlayer = engine->GetPlayerForUserID( event->GetInt( "userid" ) ); + CBaseEntity *pPlayer = UTIL_PlayerByIndex( iPlayer ); + + if ( pPlayer && pPlayer == C_DODPlayer::GetLocalDODPlayer() ) + { + IncrementCount(); + } + } + } +}; +DECLARE_ACHIEVEMENT( CAchievementDODCollectHolidayGifts, ACHIEVEMENT_DOD_COLLECT_HOLIDAY_GIFTS, "DOD_COLLECT_HOLIDAY_GIFTS", 5 ); + +#endif // CLIENT_DLL
\ No newline at end of file diff --git a/game/shared/dod/dod_gamemovement.cpp b/game/shared/dod/dod_gamemovement.cpp new file mode 100644 index 0000000..9582189 --- /dev/null +++ b/game/shared/dod/dod_gamemovement.cpp @@ -0,0 +1,1405 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +//========= Copyright � 1996-2001, Valve LLC, All rights reserved. ============ +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= +#include "cbase.h" +#include "gamemovement.h" +#include "dod_gamerules.h" +#include "dod_shareddefs.h" +#include "in_buttons.h" +#include "movevars_shared.h" + +#include "weapon_dodsniper.h" +#include "weapon_dodbaserpg.h" +#include "weapon_dodsemiauto.h" + + +#ifdef CLIENT_DLL + #include "c_dod_player.h" +#else + #include "dod_player.h" +#endif + +extern bool g_bMovementOptimizations; + +class CDODGameMovement : public CGameMovement +{ +public: + DECLARE_CLASS( CDODGameMovement, CGameMovement ); + + CDODGameMovement(); + virtual ~CDODGameMovement(); + + void SetPlayerSpeed( void ); + + virtual void ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMove ); + virtual bool CanAccelerate(); + virtual bool CheckJumpButton( void ); + virtual void ReduceTimers( void ); + virtual void WalkMove( void ); + virtual void AirMove( void ); + virtual void CheckParameters( void ); + virtual void CheckFalling( void ); + + // Ducking + virtual void Duck( void ); + virtual void FinishUnDuck( void ); + virtual void FinishDuck( void ); + virtual void HandleDuckingSpeedCrop(); + void SetDODDuckedEyeOffset( float duckFraction ); + void SetDeployedEyeOffset( void ); + + // Prone + void SetProneEyeOffset( float proneFraction ); + void FinishProne( void ); + void FinishUnProne( void ); + bool CanUnprone(); + + virtual Vector GetPlayerMins( void ) const; // uses local player + virtual Vector GetPlayerMaxs( void ) const; // uses local player + + // IGameMovement interface + virtual Vector GetPlayerMins( bool ducked ) const { return BaseClass::GetPlayerMins(ducked); } + virtual Vector GetPlayerMaxs( bool ducked ) const { return BaseClass::GetPlayerMaxs(ducked); } + virtual Vector GetPlayerViewOffset( bool ducked ) const { return BaseClass::GetPlayerViewOffset(ducked); } + + void ViewOffsetAnimation( Vector vecDest, float flTime, ViewAnimationType type ); + + void SetViewOffset( Vector vecViewOffset ); + + virtual unsigned int PlayerSolidMask( bool brushOnly = false ); + +protected: + virtual void PlayerMove(); + + void CheckForLadders( bool wasOnGround ); + bool ResolveStanding( void ); + void TracePlayerBBoxWithStep( const Vector &vStart, const Vector &vEnd, unsigned int fMask, int collisionGroup, trace_t &trace ); + +public: + CDODPlayer *m_pDODPlayer; + bool m_bUnProneToDuck; +}; + + +// Expose our interface. +static CDODGameMovement g_GameMovement; +IGameMovement *g_pGameMovement = ( IGameMovement * )&g_GameMovement; + +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CGameMovement, IGameMovement,INTERFACENAME_GAMEMOVEMENT, g_GameMovement ); + + +// ---------------------------------------------------------------------------------------- // +// CDODGameMovement. +// ---------------------------------------------------------------------------------------- // + +CDODGameMovement::CDODGameMovement() +{ + // Don't set any member variables here, or you'll get an access + // violation exception on LoadLibrary, and will have to stay up til + // 3 in the morning figuring out where you did bad things. + + m_bUnProneToDuck = false; +} + +CDODGameMovement::~CDODGameMovement() +{ +} + +void CDODGameMovement::SetPlayerSpeed( void ) +{ + if( DODGameRules()->State_Get() == STATE_PREROUND ) + { + mv->m_flClientMaxSpeed = PLAYER_SPEED_FROZEN; + return; + } + + if ( m_pDODPlayer->m_Shared.IsPlanting() || + m_pDODPlayer->m_Shared.IsDefusing() ) + { + mv->m_flClientMaxSpeed = PLAYER_SPEED_FROZEN; + return; + } + + bool bZoomed = ( m_pDODPlayer->GetFOV() < m_pDODPlayer->GetDefaultFOV() ); + bool bBazookaDeployed = false; + bool bZoomingIn = false; + + CWeaponDODBase *pWpn = m_pDODPlayer->GetActiveDODWeapon(); + if( pWpn ) + { + if( pWpn->GetDODWpnData().m_WeaponType == WPN_TYPE_BAZOOKA ) + { + CDODBaseRocketWeapon *pBazooka = (CDODBaseRocketWeapon *)pWpn; + bBazookaDeployed = pBazooka->ShouldPlayerBeSlow(); + } + + if ( pWpn->GetDODWpnData().m_WeaponType == WPN_TYPE_SNIPER ) + { + CDODSniperWeapon *pSniper = dynamic_cast<CDODSniperWeapon *>( pWpn ); + if ( pSniper ) + { + bZoomingIn = !bZoomed && pSniper->IsZoomingIn(); + } + } + } + + // are we zooming? + + if ( m_pDODPlayer->m_Shared.IsInMGDeploy() ) + { + mv->m_flClientMaxSpeed = PLAYER_SPEED_FROZEN; + } + else if ( m_pDODPlayer->m_Shared.IsProne() && + !m_pDODPlayer->m_Shared.IsGettingUpFromProne() && + m_pDODPlayer->GetGroundEntity() != NULL ) + { + if ( bZoomed ) + mv->m_flClientMaxSpeed = PLAYER_SPEED_PRONE_ZOOMED; + else if ( bBazookaDeployed ) + mv->m_flClientMaxSpeed = PLAYER_SPEED_PRONE_BAZOOKA_DEPLOYED; + else + mv->m_flClientMaxSpeed = PLAYER_SPEED_PRONE; //Base prone speed + } + else //not prone, dead or deployed - standing or crouching and possibly moving + { + float stamina = m_pDODPlayer->m_Shared.GetStamina(); + + if ( bZoomed ) + { + mv->m_flClientMaxSpeed = PLAYER_SPEED_ZOOMED; + } + else if ( bBazookaDeployed ) + { + mv->m_flClientMaxSpeed = PLAYER_SPEED_BAZOOKA_DEPLOYED; + } + else if ( mv->m_nButtons & IN_DUCK ) + { + mv->m_flClientMaxSpeed = PLAYER_SPEED_RUN; //gets cut in fraction later + } + // check for slowed from leg hit or firing a machine gun + else if ( m_pDODPlayer->m_Shared.GetSlowedTime() > gpGlobals->curtime ) + { + mv->m_flClientMaxSpeed = PLAYER_SPEED_SLOWED; + } + else + { + float flMaxSpeed; + + if ( ( mv->m_nButtons & IN_SPEED ) && ( stamina > 0 ) && ( mv->m_nButtons & IN_FORWARD ) && !bZoomingIn ) + { + flMaxSpeed = PLAYER_SPEED_SPRINT; //sprinting + } + else + { + flMaxSpeed = PLAYER_SPEED_RUN; //jogging + } + + mv->m_flClientMaxSpeed = flMaxSpeed - 100 + stamina; + } + } + + if ( m_pDODPlayer->GetGroundEntity() != NULL ) + { + if( m_pDODPlayer->m_Shared.IsGoingProne() ) + { + float pronetime = m_pDODPlayer->m_Shared.m_flGoProneTime - gpGlobals->curtime; + + //interp to prone speed + float flProneFraction = SimpleSpline( pronetime / TIME_TO_PRONE ); + + float maxSpeed = mv->m_flClientMaxSpeed; + + if ( m_bUnProneToDuck ) + maxSpeed *= 0.33; + + mv->m_flClientMaxSpeed = ( ( 1 - flProneFraction ) * PLAYER_SPEED_PRONE ) + ( flProneFraction * maxSpeed ); + } + else if( m_pDODPlayer->m_Shared.IsGettingUpFromProne() ) + { + float pronetime = m_pDODPlayer->m_Shared.m_flUnProneTime - gpGlobals->curtime; + + //interp to regular speed speed + float flProneFraction = SimpleSpline( pronetime / TIME_TO_PRONE ); + + float maxSpeed = mv->m_flClientMaxSpeed; + + if ( m_bUnProneToDuck ) + maxSpeed *= 0.33; + + mv->m_flClientMaxSpeed = ( flProneFraction * PLAYER_SPEED_PRONE ) + ( ( 1 - flProneFraction ) * maxSpeed ); + } + } +} + +ConVar cl_show_speed( "cl_show_speed", "0", FCVAR_CHEAT | FCVAR_REPLICATED, "spam console with local player speed" ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDODGameMovement::CheckParameters( void ) +{ + QAngle v_angle; + + SetPlayerSpeed(); + + if ( player->GetMoveType() != MOVETYPE_ISOMETRIC && + player->GetMoveType() != MOVETYPE_NOCLIP && + player->GetMoveType() != MOVETYPE_OBSERVER ) + { + float spd; + float maxspeed; + + spd = ( mv->m_flForwardMove * mv->m_flForwardMove ) + + ( mv->m_flSideMove * mv->m_flSideMove ) + + ( mv->m_flUpMove * mv->m_flUpMove ); + + maxspeed = mv->m_flClientMaxSpeed; + if ( maxspeed != 0.0 ) + { + mv->m_flMaxSpeed = MIN( maxspeed, mv->m_flMaxSpeed ); + } + + // Slow down by the speed factor + float flSpeedFactor = 1.0f; + if ( player->GetSurfaceData() ) + { + flSpeedFactor = player->GetSurfaceData()->game.maxSpeedFactor; + } + + // If we have a constraint, slow down because of that too. + float flConstraintSpeedFactor = ComputeConstraintSpeedFactor(); + if (flConstraintSpeedFactor < flSpeedFactor) + flSpeedFactor = flConstraintSpeedFactor; + + mv->m_flMaxSpeed *= flSpeedFactor; + + if ( g_bMovementOptimizations ) + { + // Same thing but only do the sqrt if we have to. + if ( ( spd != 0.0 ) && ( spd > mv->m_flMaxSpeed*mv->m_flMaxSpeed ) ) + { + float fRatio = mv->m_flMaxSpeed / sqrt( spd ); + mv->m_flForwardMove *= fRatio; + mv->m_flSideMove *= fRatio; + mv->m_flUpMove *= fRatio; + } + } + else + { + spd = sqrt( spd ); + if ( ( spd != 0.0 ) && ( spd > mv->m_flMaxSpeed ) ) + { + float fRatio = mv->m_flMaxSpeed / spd; + mv->m_flForwardMove *= fRatio; + mv->m_flSideMove *= fRatio; + mv->m_flUpMove *= fRatio; + } + } + } + + + if ( player->GetFlags() & FL_FROZEN || + player->GetFlags() & FL_ONTRAIN || + IsDead() ) + { + mv->m_flForwardMove = 0; + mv->m_flSideMove = 0; + mv->m_flUpMove = 0; + } + + DecayPunchAngle(); + + // Take angles from command. + if ( !IsDead() ) + { + v_angle = mv->m_vecAngles; + v_angle = v_angle + player->m_Local.m_vecPunchAngle; + + // Now adjust roll angle + if ( player->GetMoveType() != MOVETYPE_ISOMETRIC && + player->GetMoveType() != MOVETYPE_NOCLIP ) + { + mv->m_vecAngles[ROLL] = CalcRoll( v_angle, mv->m_vecVelocity, sv_rollangle.GetFloat(), sv_rollspeed.GetFloat() ); + } + else + { + mv->m_vecAngles[ROLL] = 0.0; // v_angle[ ROLL ]; + } + mv->m_vecAngles[PITCH] = v_angle[PITCH]; + mv->m_vecAngles[YAW] = v_angle[YAW]; + } + else + { + mv->m_vecAngles = mv->m_vecOldAngles; + } + + // Set dead player view_offset + if ( IsDead() ) + { + SetViewOffset( VEC_DEAD_VIEWHEIGHT_SCALED( player ) ); + } + + // Adjust client view angles to match values used on server. + if ( mv->m_vecAngles[YAW] > 180.0f ) + { + mv->m_vecAngles[YAW] -= 360.0f; + } + + if ( cl_show_speed.GetBool() ) + { + Vector vel = m_pDODPlayer->GetAbsVelocity(); + float actual_speed = sqrt( vel.x * vel.x + vel.y * vel.y ); + Msg( "player speed %.1f\n",actual_speed ); + } +} + +void CDODGameMovement::CheckFalling( void ) +{ + // if we landed on the ground + if ( player->GetGroundEntity() != NULL && !IsDead() ) + { + if ( player->m_Local.m_flFallVelocity >= 300 ) + { + CPASFilter filter( player->GetAbsOrigin() ); + filter.UsePredictionRules(); + player->EmitSound( filter, player->entindex(), "Player.JumpLanding" ); + } + + // turn off the jumping flag if we're on ground after a jump + if ( m_pDODPlayer->m_Shared.IsJumping() ) + { + m_pDODPlayer->m_Shared.SetJumping( false ); + + // if we landed from a jump, slow us + if ( player->m_Local.m_flFallVelocity > 50 ) + m_pDODPlayer->m_Shared.SetSlowedTime( 0.5 ); + } + } + + BaseClass::CheckFalling(); +} + + +void CDODGameMovement::ProcessMovement( CBasePlayer *pBasePlayer, CMoveData *pMove ) +{ + //Store the player pointer + m_pDODPlayer = ToDODPlayer( pBasePlayer ); + Assert( m_pDODPlayer ); + + m_pDODPlayer->m_Shared.ViewAnimThink(); + + BaseClass::ProcessMovement( pBasePlayer, pMove ); +} + +bool CDODGameMovement::CanAccelerate() +{ + // Only allow the player to accelerate when in certain states. + DODPlayerState curState = m_pDODPlayer->State_Get(); + if ( curState == STATE_ACTIVE ) + { + return player->GetWaterJumpTime() == 0; + } + else if ( player->IsObserver() ) + { + return true; + } + else + { + return false; + } +} + +void CDODGameMovement::PlayerMove() +{ + BaseClass::PlayerMove(); + + if ( player->GetMoveType() != MOVETYPE_ISOMETRIC && + player->GetMoveType() != MOVETYPE_NOCLIP && + player->GetMoveType() != MOVETYPE_OBSERVER ) + { + + // Cap actual player speed, fix wall running + float spd = mv->m_vecVelocity[0] * mv->m_vecVelocity[0] + + mv->m_vecVelocity[1] * mv->m_vecVelocity[1]; + + if( spd > 0.0 && spd > ( mv->m_flMaxSpeed * mv->m_flMaxSpeed ) ) + { + float fRatio = mv->m_flMaxSpeed / sqrt( spd ); + mv->m_vecVelocity[0] *= fRatio; + mv->m_vecVelocity[1] *= fRatio; + } + } +} + + +void CDODGameMovement::WalkMove( void ) +{ + float flSpeed = m_pDODPlayer->GetAbsVelocity().Length2D(); + + bool bSprintButtonPressed = ( mv->m_nButtons & IN_SPEED ) > 0; + + if( bSprintButtonPressed && + ( mv->m_nButtons & IN_FORWARD ) && + !m_pDODPlayer->m_Shared.IsProne() && + !m_pDODPlayer->m_Shared.IsDucking() && + flSpeed > 80 ) + { + m_pDODPlayer->SetSprinting( true ); + } + else + { + m_pDODPlayer->SetSprinting( false ); + } + + BaseClass::WalkMove(); + + //CheckForLadders( true ); + //ResolveStanding(); +} + +//----------------------------------------------------------------------------- +// Purpose: Allow bots etc to use slightly different solid masks +//----------------------------------------------------------------------------- +unsigned int CDODGameMovement::PlayerSolidMask( bool brushOnly ) +{ + int mask = 0; + + switch ( m_pDODPlayer->GetTeamNumber() ) + { + case TEAM_ALLIES: + mask = CONTENTS_TEAM1; + break; + + case TEAM_AXIS: + mask = CONTENTS_TEAM2; + break; + } + + return ( mask | BaseClass::PlayerSolidMask( brushOnly ) ); +} + +void CDODGameMovement::AirMove( void ) +{ + BaseClass::AirMove(); + + //CheckForLadders( false ); +} + +void CDODGameMovement::CheckForLadders( bool wasOnGround ) +{ + if ( !wasOnGround || !ResolveStanding() ) + { + trace_t trace; + TracePlayerBBox( mv->GetAbsOrigin(), mv->GetAbsOrigin() + Vector( 0, 0, -5), MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace ); + if ( trace.fraction == 1.0f ) + { + Vector vel = -m_pDODPlayer->m_lastStandingPos + mv->GetAbsOrigin(); + if ( !vel.x && !vel.y ) + { + return; + } + vel.NormalizeInPlace(); + vel *= 5.0f; + Vector vecStartPos( mv->GetAbsOrigin().x + vel.x, mv->GetAbsOrigin().y + vel.y, mv->GetAbsOrigin().z ); + vel *= 5.0f; + Vector vecStandPos( mv->GetAbsOrigin().x - vel.x, mv->GetAbsOrigin().y - vel.y, mv->GetAbsOrigin().z - ( player->m_Local.m_flStepSize * 1.0f ) ); + + TracePlayerBBoxWithStep( vecStartPos, vecStandPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace ); + + if ( trace.fraction != 1.0f && OnLadder( trace ) && trace.plane.normal.z != 1.0f ) + { + if ( trace.fraction > 0.6 ) + { + vel.NormalizeInPlace(); + Vector org = mv->GetAbsOrigin(); + org -= vel*5; + mv->SetAbsOrigin( org ); + } + player->SetMoveType( MOVETYPE_LADDER ); + player->SetMoveCollide( MOVECOLLIDE_DEFAULT ); + + player->SetLadderNormal( trace.plane.normal ); + mv->m_vecVelocity.Init(); + } + } + } + else + { + m_pDODPlayer->m_lastStandingPos = mv->GetAbsOrigin(); + } +} + +inline void CDODGameMovement::TracePlayerBBoxWithStep( const Vector &vStart, const Vector &vEnd, + unsigned int fMask, int collisionGroup, trace_t &trace ) +{ + VPROF( "CDODGameMovement::TracePlayerBBoxWithStep" ); + + Vector vHullMin = GetPlayerMins( player->m_Local.m_bDucked ); + vHullMin.z += player->m_Local.m_flStepSize; + Vector vHullMax = GetPlayerMaxs( player->m_Local.m_bDucked ); + + Ray_t ray; + ray.Init( vStart, vEnd, vHullMin, vHullMax ); + UTIL_TraceRay( ray, fMask, mv->m_nPlayerHandle.Get(), collisionGroup, &trace ); +} + +// Taken from TF2 to prevent bouncing down slopes +bool CDODGameMovement::ResolveStanding( void ) +{ + VPROF( "CDODGameMovement::ResolveStanding" ); + + // + // Attempt to move down twice your step height. Anything between 0.5 and 1.0 + // is a valid "stand" value. + // + + // Matt - don't move twice your step height, only check a little bit down + // this will keep us relatively glued to stairs without feeling too snappy + float flMaxStepDrop = 8.0f; + + Vector vecStandPos( mv->GetAbsOrigin().x, mv->GetAbsOrigin().y, mv->GetAbsOrigin().z - ( flMaxStepDrop ) ); + + trace_t trace; + TracePlayerBBoxWithStep( mv->GetAbsOrigin(), vecStandPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace ); + + // Anything between 0.5 and 1.0 is a valid stand value + if ( fabs( trace.fraction - 0.5 ) < 0.0004f ) + { + return true; + } + + if ( trace.fraction > 0.5f ) + { + trace.fraction -= 0.5f; + Vector vecNewOrigin; + vecNewOrigin = mv->GetAbsOrigin() + trace.fraction * ( vecStandPos - mv->GetAbsOrigin() ); + mv->SetAbsOrigin( vecNewOrigin ); + return false; + } + + // Less than 0.5 mean we need to attempt to push up the difference. + vecStandPos.z = ( mv->GetAbsOrigin().z + ( ( 0.5f - trace.fraction ) * ( player->m_Local.m_flStepSize * 2.0f ) ) ); + TracePlayerBBoxWithStep( mv->GetAbsOrigin(), vecStandPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace ); + + // A fraction of 1.0 means we stood up fine - done. + if ( trace.fraction == 1.0f ) + { + mv->SetAbsOrigin( trace.endpos ); + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Recover stamina +//----------------------------------------------------------------------------- +void CDODGameMovement::ReduceTimers( void ) +{ + Vector vecPlayerVelocity = m_pDODPlayer->GetAbsVelocity(); + float flStamina = m_pDODPlayer->m_Shared.GetStamina(); + float fl2DVelocitySquared = vecPlayerVelocity.x * vecPlayerVelocity.x + + vecPlayerVelocity.y * vecPlayerVelocity.y; + + if ( !( mv->m_nButtons & IN_SPEED ) ) + { + m_pDODPlayer->m_Shared.ResetSprintPenalty(); + } + + // Can only sprint in forward direction. + bool bSprinting = ( (mv->m_nButtons & IN_SPEED) && ( mv->m_nButtons & IN_FORWARD ) ); + + // If we're holding the sprint key and also actually moving, remove some stamina + Vector vel = m_pDODPlayer->GetAbsVelocity(); + if ( bSprinting && fl2DVelocitySquared > 10000 ) //speed > 100 + { + //flStamina -= 30 * gpGlobals->frametime; //reduction for sprinting + //flStamina += 10 * gpGlobals->frametime; //addition for recovering + + flStamina -= 20 * gpGlobals->frametime; + + m_pDODPlayer->m_Shared.SetStamina( flStamina ); + } + else + { + //gain some back + if ( fl2DVelocitySquared <= 0 ) + { + flStamina += 60 * gpGlobals->frametime; + } + else if ( ( m_pDODPlayer->GetFlags() & FL_ONGROUND ) && + ( mv->m_nButtons & IN_DUCK ) && + ( m_pDODPlayer->GetFlags() & FL_DUCKING ) ) + { + flStamina += 50 * gpGlobals->frametime; + } + else + { + flStamina += 10 * gpGlobals->frametime; + } + + m_pDODPlayer->m_Shared.SetStamina( flStamina ); + } + +#ifdef CLIENT_DLL + if ( bSprinting && flStamina < 25 ) + { + m_pDODPlayer->HintMessage( HINT_LOW_STAMINA ); + } +#endif + + BaseClass::ReduceTimers(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDODGameMovement::CheckJumpButton( void ) +{ + if (m_pDODPlayer->pl.deadflag) + { + mv->m_nOldButtons |= IN_JUMP ; // don't jump again until released + return false; + } + + if( m_pDODPlayer->m_Shared.IsProne() || + m_pDODPlayer->m_Shared.IsGettingUpFromProne() || + m_pDODPlayer->m_Shared.IsGoingProne() ) + { + mv->m_nOldButtons |= IN_JUMP; + return false; + } + + // See if we are waterjumping. If so, decrement count and return. + float flWaterJumpTime = player->GetWaterJumpTime(); + + if ( flWaterJumpTime > 0 ) + { + flWaterJumpTime -= gpGlobals->frametime; + if (flWaterJumpTime < 0) + flWaterJumpTime = 0; + + player->SetWaterJumpTime( flWaterJumpTime ); + + return false; + } + + // If we are in the water most of the way... + if ( m_pDODPlayer->GetWaterLevel() >= 2 ) + { + // swimming, not jumping + SetGroundEntity( NULL ); + + if(m_pDODPlayer->GetWaterType() == CONTENTS_WATER) // We move up a certain amount + mv->m_vecVelocity[2] = 100; + else if (m_pDODPlayer->GetWaterType() == CONTENTS_SLIME) + mv->m_vecVelocity[2] = 80; + + // play swiming sound + if ( player->GetSwimSoundTime() <= 0 ) + { + // Don't play sound again for 1 second + player->SetSwimSoundTime( 1000 ); + PlaySwimSound(); + } + + return false; + } + + // No more effect + if (m_pDODPlayer->GetGroundEntity() == NULL) + { + mv->m_nOldButtons |= IN_JUMP; + return false; // in air, so no effect + } + + if ( mv->m_nOldButtons & IN_JUMP ) + return false; // don't pogo stick + + if( m_pDODPlayer->m_Shared.IsInMGDeploy() ) + { + return false; + } + + // In the air now. + SetGroundEntity( NULL ); + + m_pDODPlayer->PlayStepSound( (Vector &)mv->GetAbsOrigin(), player->GetSurfaceData(), 1.0, true ); + + m_pDODPlayer->DoAnimationEvent( PLAYERANIMEVENT_JUMP ); + + // make the jump sound + CPASFilter filter( m_pDODPlayer->GetAbsOrigin() ); + filter.UsePredictionRules(); + m_pDODPlayer->EmitSound( filter, m_pDODPlayer->entindex(), "Player.Jump" ); + + float flGroundFactor = 1.0f; + if ( player->GetSurfaceData() ) + { + flGroundFactor = player->GetSurfaceData()->game.jumpFactor; + } + + /* + // old and busted + + float flStamina = m_pDODPlayer->m_Shared.GetStamina(); + + //15.0 is the base height. the player will always jump this high + //regardless of stamina. Also the player will be able to jump max height + //until he is below 60 stamina. Then the height will decrease proportionately + + float flJumpSpeed = 15.0; //base jump height + + if( flStamina >= 60.0f ) + { + flJumpSpeed += 30.0; + } + else + { + flJumpSpeed += (30.0 * ( flStamina / 60.0f ) ); + } + + //Remove stamina for a successful jump + m_pDODPlayer->m_Shared.SetStamina( flStamina - 40 ); + + */ + + // new hotness - constant jumpspeed of 45 + //m_pDODPlayer->m_Shared.SetSlowedTime( 1.0f ); + + Assert( GetCurrentGravity() == 800.0f ); + + // Accelerate upward + // If we are ducking... + float startz = mv->m_vecVelocity[2]; + if ( ( m_pDODPlayer->m_Local.m_bDucking ) || ( m_pDODPlayer->GetFlags() & FL_DUCKING ) ) + { + // d = 0.5 * g * t^2 - distance traveled with linear accel + // t = sqrt(2.0 * 45 / g) - how long to fall 45 units + // v = g * t - velocity at the end (just invert it to jump up that high) + // v = g * sqrt(2.0 * 45 / g ) + // v^2 = g * g * 2.0 * 45 / g + // v = sqrt( g * 2.0 * 45 ) + + mv->m_vecVelocity[2] = flGroundFactor * 268.3281572999747f; // flJumpSpeed of 45 + //mv->m_vecVelocity[2] = flGroundFactor * sqrt(2 * 800 * flJumpSpeed); // 2 * gravity * height + } + else + { + mv->m_vecVelocity[2] += flGroundFactor * 268.3281572999747f; // flJumpSpeed of 45 + //mv->m_vecVelocity[2] += flGroundFactor * sqrt(2 * 800 * flJumpSpeed); // 2 * gravity * height + } + + FinishGravity(); + + mv->m_outWishVel.z += mv->m_vecVelocity[2] - startz; + mv->m_outStepHeight += 0.1f; + + // Flag that we jumped. + mv->m_nOldButtons |= IN_JUMP; // don't jump again until released + + m_pDODPlayer->m_Shared.SetJumping( true ); + + return true; +} + + + +//----------------------------------------------------------------------------- +// Purpose: Limit speed if we are ducking +//----------------------------------------------------------------------------- +void CDODGameMovement::HandleDuckingSpeedCrop() +{ + if ( !( m_iSpeedCropped & SPEED_CROPPED_DUCK ) ) + { + if ( ( mv->m_nButtons & IN_DUCK ) || ( player->m_Local.m_bDucking ) || ( player->GetFlags() & FL_DUCKING ) ) + { + float frac = 0.33333333f; + mv->m_flForwardMove *= frac; + mv->m_flSideMove *= frac; + mv->m_flUpMove *= frac; + m_iSpeedCropped |= SPEED_CROPPED_DUCK; + } + } +} + +bool CDODGameMovement::CanUnprone() +{ + int i; + trace_t trace; + Vector newOrigin; + + VectorCopy( mv->GetAbsOrigin(), newOrigin ); + + Vector vecMins, vecMaxs; + + if ( mv->m_nButtons & IN_DUCK ) + { + vecMins = VEC_DUCK_HULL_MIN_SCALED( player ); + vecMaxs = VEC_DUCK_HULL_MAX_SCALED( player ); + } + else + { + vecMins = VEC_HULL_MIN_SCALED( player ); + vecMaxs = VEC_HULL_MAX_SCALED( player ); + } + + if ( player->GetGroundEntity() != NULL ) + { + for ( i = 0; i < 3; i++ ) + { + newOrigin[i] += ( VEC_PRONE_HULL_MIN_SCALED( player )[i] - vecMins[i] ); + } + } + else + { + // If in air an letting go of crouch, make sure we can offset origin to make + // up for uncrouching + + Vector hullSizeNormal = vecMaxs - vecMins; + Vector hullSizeProne = VEC_PRONE_HULL_MAX_SCALED( player ) - VEC_PRONE_HULL_MIN_SCALED( player ); + + Vector viewDelta = -0.5f * ( hullSizeNormal - hullSizeProne ); + + VectorAdd( newOrigin, viewDelta, newOrigin ); + } + + bool saveprone = m_pDODPlayer->m_Shared.IsProne(); + bool saveducked = player->m_Local.m_bDucked; + + // pretend we're not prone + m_pDODPlayer->m_Shared.SetProne( false ); + if ( mv->m_nButtons & IN_DUCK ) + player->m_Local.m_bDucked = true; + + TracePlayerBBox( mv->GetAbsOrigin(), newOrigin, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace ); + + // revert to reality + m_pDODPlayer->m_Shared.SetProne( saveprone ); + player->m_Local.m_bDucked = saveducked; + + if ( trace.startsolid || ( trace.fraction != 1.0f ) ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Stop ducking +//----------------------------------------------------------------------------- +void CDODGameMovement::FinishUnDuck( void ) +{ + int i; + trace_t trace; + Vector newOrigin; + + VectorCopy( mv->GetAbsOrigin(), newOrigin ); + + if ( player->GetGroundEntity() != NULL ) + { + for ( i = 0; i < 3; i++ ) + { + newOrigin[i] += ( VEC_DUCK_HULL_MIN_SCALED( player )[i] - VEC_HULL_MIN_SCALED( player )[i] ); + } + } + else + { + // If in air an letting go of crouch, make sure we can offset origin to make + // up for uncrouching + // orange box patch - made this match the check in CanUnduck() + Vector hullSizeNormal = VEC_HULL_MAX_SCALED( player ) - VEC_HULL_MIN_SCALED( player ); + Vector hullSizeCrouch = VEC_DUCK_HULL_MAX_SCALED( player ) - VEC_DUCK_HULL_MIN_SCALED( player ); + Vector viewDelta = ( hullSizeNormal - hullSizeCrouch ); + viewDelta.Negate(); + VectorAdd( newOrigin, viewDelta, newOrigin ); + } + + player->m_Local.m_bDucked = false; + player->RemoveFlag( FL_DUCKING ); + player->m_Local.m_bDucking = false; + SetViewOffset( GetPlayerViewOffset( false ) ); + player->m_Local.m_flDucktime = 0; + + mv->SetAbsOrigin( newOrigin ); + + // Recategorize position since ducking can change origin + CategorizePosition(); +} + +//----------------------------------------------------------------------------- +// Purpose: Finish ducking +//----------------------------------------------------------------------------- +void CDODGameMovement::FinishDuck( void ) +{ + Vector hullSizeNormal = VEC_HULL_MAX_SCALED( player ) - VEC_HULL_MIN_SCALED( player ); + Vector hullSizeCrouch = VEC_DUCK_HULL_MAX_SCALED( player ) - VEC_DUCK_HULL_MIN_SCALED( player ); + + Vector viewDelta = 0.5f * ( hullSizeNormal - hullSizeCrouch ); + + SetViewOffset( GetPlayerViewOffset( true ) ); + player->AddFlag( FL_DUCKING ); + player->m_Local.m_bDucking = false; + + if ( !player->m_Local.m_bDucked ) + { + Vector org = mv->GetAbsOrigin(); + + if ( player->GetGroundEntity() != NULL ) + { + org -= VEC_DUCK_HULL_MIN_SCALED( player ) - VEC_HULL_MIN_SCALED( player ); + } + else + { + VectorAdd( org, viewDelta, org ); + } + mv->SetAbsOrigin( org ); + + player->m_Local.m_bDucked = true; + } + + // See if we are stuck? + FixPlayerCrouchStuck( true ); + + // Recategorize position since ducking can change origin + CategorizePosition(); +} + +// Being deployed or undeploying totally stomps the duck view offset +void CDODGameMovement::SetDeployedEyeOffset( void ) +{ + if ( m_pDODPlayer->m_Shared.IsProne() || m_pDODPlayer->m_Shared.IsGettingUpFromProne() ) + return; + + if ( !m_pDODPlayer->IsAlive() ) + return; + + float flTimeSinceDeployChange = gpGlobals->curtime - m_pDODPlayer->m_Shared.m_flDeployChangeTime; + + if ( m_pDODPlayer->m_Shared.IsInMGDeploy() || flTimeSinceDeployChange < TIME_TO_DEPLOY ) + { + if ( m_pDODPlayer->m_Shared.IsInMGDeploy() ) + { + // anim to deployed + if ( m_pDODPlayer->m_Shared.GetLastViewAnimTime() < m_pDODPlayer->m_Shared.m_flDeployChangeTime.m_Value ) + { + // Deployed height + float flViewOffset = clamp( m_pDODPlayer->m_Shared.GetDeployedHeight(), + CROUCHING_DEPLOY_HEIGHT, + STANDING_DEPLOY_HEIGHT ); + + Vector vecView = player->GetViewOffset(); + vecView.z = flViewOffset; + + ViewOffsetAnimation( vecView, TIME_TO_DEPLOY, VIEW_ANIM_LINEAR_Z_ONLY ); + m_pDODPlayer->m_Shared.SetLastViewAnimTime( gpGlobals->curtime ); + } + } + else + { + // anim to undeployed + if ( m_pDODPlayer->m_Shared.GetLastViewAnimTime() < m_pDODPlayer->m_Shared.m_flDeployChangeTime.m_Value ) + { + ViewOffsetAnimation( GetPlayerViewOffset( player->m_Local.m_bDucked ), TIME_TO_DEPLOY, VIEW_ANIM_LINEAR_Z_ONLY ); + m_pDODPlayer->m_Shared.SetLastViewAnimTime( gpGlobals->curtime ); + } + } + + if ( flTimeSinceDeployChange >= TIME_TO_DEPLOY ) + { + player->m_Local.m_bDucked = false; + player->RemoveFlag( FL_DUCKING ); + player->m_Local.m_bDucking = false; + player->m_Local.m_flDucktime = 0; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : duckFraction - +//----------------------------------------------------------------------------- +void CDODGameMovement::SetDODDuckedEyeOffset( float duckFraction ) +{ + // Different from CGameMovement in that + Vector vDuckHullMin = GetPlayerMins( true ); + Vector vStandHullMin = GetPlayerMins( false ); + + float fMore = ( vDuckHullMin.z - vStandHullMin.z ); + + Vector vecStandViewOffset = GetPlayerViewOffset( false ); + + Vector vecDuckViewOffset = GetPlayerViewOffset( true ); + Vector temp = player->GetViewOffset(); + temp.z = ( ( vecDuckViewOffset.z - fMore ) * duckFraction ) + + ( vecStandViewOffset.z * ( 1 - duckFraction ) ); + SetViewOffset( temp ); +} + +void CDODGameMovement::SetProneEyeOffset( float proneFraction ) +{ + Vector vecPropViewOffset = VEC_PRONE_VIEW; + Vector vecStandViewOffset = GetPlayerViewOffset( player->m_Local.m_bDucked ); + + Vector temp = player->GetViewOffset(); + temp.z = SimpleSplineRemapVal( proneFraction, 1.0, 0.0, vecPropViewOffset.z, vecStandViewOffset.z ); + + SetViewOffset( temp ); +} + +void CDODGameMovement::FinishUnProne( void ) +{ + m_pDODPlayer->m_Shared.m_flUnProneTime = 0.0f; + + SetProneEyeOffset( 0.0 ); + + Vector vHullMin = GetPlayerMins( player->m_Local.m_bDucked ); + Vector vHullMax = GetPlayerMaxs( player->m_Local.m_bDucked ); + + if ( m_bUnProneToDuck ) + { + FinishDuck(); + } + else + { + CategorizePosition(); + + if ( mv->m_nButtons & IN_DUCK && !( player->GetFlags() & FL_DUCKING ) ) + { + // Use 1 second so super long jump will work + player->m_Local.m_flDucktime = 1000; + player->m_Local.m_bDucking = true; + } + } +} + +void CDODGameMovement::FinishProne( void ) +{ + m_pDODPlayer->m_Shared.SetProne( true ); + m_pDODPlayer->m_Shared.m_flGoProneTime = 0.0f; + +#ifndef CLIENT_DLL + m_pDODPlayer->HintMessage( HINT_PRONE ); +#endif + + FinishUnDuck(); // clear ducking + + SetProneEyeOffset( 1.0 ); + + FixPlayerCrouchStuck(true); + + CategorizePosition(); +} + +//----------------------------------------------------------------------------- +// Purpose: See if duck button is pressed and do the appropriate things +//----------------------------------------------------------------------------- +void CDODGameMovement::Duck( void ) +{ + int buttonsChanged = ( mv->m_nOldButtons ^ mv->m_nButtons ); // These buttons have changed this frame + int buttonsPressed = buttonsChanged & mv->m_nButtons; // The changed ones still down are "pressed" + int buttonsReleased = buttonsChanged & mv->m_nOldButtons; // The changed ones which were previously down are "released" + + if ( mv->m_nButtons & IN_DUCK ) + { + mv->m_nOldButtons |= IN_DUCK; + } + else + { + mv->m_nOldButtons &= ~IN_DUCK; + } + + if ( !player->IsAlive() ) + { + if( m_pDODPlayer->m_Shared.IsProne() ) + { + FinishUnProne(); + } + + // Unduck + if ( player->m_Local.m_bDucking || player->m_Local.m_bDucked ) + { + FinishUnDuck(); + } + return; + } + + static int iState = 0; + + // Prone / UnProne - we don't duck or deploy if this is happening + if( m_pDODPlayer->m_Shared.m_flUnProneTime > 0.0f ) + { + float pronetime = m_pDODPlayer->m_Shared.m_flUnProneTime - gpGlobals->curtime; + + if( pronetime < 0 ) + { + FinishUnProne(); + + if ( !m_bUnProneToDuck && ( mv->m_nButtons & IN_DUCK ) ) + { + buttonsPressed |= IN_DUCK; + mv->m_nOldButtons &= ~IN_DUCK; + } + } + + // Set these, so that as soon as we stop unproning, we don't pop to standing + // the information that we let go of the duck key has been lost by now. + if ( m_bUnProneToDuck ) + { + player->m_Local.m_flDucktime = 1000; + player->m_Local.m_bDucking = true; + } + + //don't deal with ducking while we're proning + return; + } + else if( m_pDODPlayer->m_Shared.m_flGoProneTime > 0.0f ) + { + float pronetime = m_pDODPlayer->m_Shared.m_flGoProneTime - gpGlobals->curtime; + + if( pronetime < 0 ) + { + FinishProne(); + } + + //don't deal with ducking while we're proning + return; + } + + if ( gpGlobals->curtime > m_pDODPlayer->m_Shared.m_flNextProneCheck ) + { + if ( buttonsPressed & IN_ALT1 && m_pDODPlayer->m_Shared.CanChangePosition() ) + { + if( m_pDODPlayer->m_Shared.IsProne() == false && + m_pDODPlayer->m_Shared.IsGettingUpFromProne() == false ) + { + m_pDODPlayer->m_Shared.StartGoingProne(); + + // do unprone anim + ViewOffsetAnimation( VEC_PRONE_VIEW, TIME_TO_PRONE, VIEW_ANIM_EXPONENTIAL_Z_ONLY ); + } + else if( CanUnprone() ) + { + m_pDODPlayer->m_Shared.SetProne( false ); + m_pDODPlayer->m_Shared.StandUpFromProne(); + + // do unprone anim + ViewOffsetAnimation( GetPlayerViewOffset( m_bUnProneToDuck ), + TIME_TO_PRONE, + VIEW_ANIM_EXPONENTIAL_Z_ONLY ); + + m_bUnProneToDuck = ( mv->m_nButtons & IN_DUCK ) > 0; + } + + m_pDODPlayer->m_Shared.m_flNextProneCheck = gpGlobals->curtime + 1.0f; + return; + } + } + + SetDeployedEyeOffset(); + + if ( m_pDODPlayer->m_Shared.IsProne() && + m_pDODPlayer->m_Shared.CanChangePosition() && + !m_pDODPlayer->m_Shared.IsGettingUpFromProne() && + ( buttonsPressed & IN_DUCK ) && + CanUnprone() ) // BUGBUG - even calling this will unzoom snipers. + { + // If the player presses duck while prone, + // unprone them to the duck position + m_pDODPlayer->m_Shared.SetProne( false ); + m_pDODPlayer->m_Shared.StandUpFromProne(); + + m_bUnProneToDuck = true; + + // do unprone anim + ViewOffsetAnimation( GetPlayerViewOffset( m_bUnProneToDuck ), + TIME_TO_PRONE, + VIEW_ANIM_EXPONENTIAL_Z_ONLY ); + + // simulate a duck that was pressed while we were prone + player->AddFlag( FL_DUCKING ); + player->m_Local.m_bDucked = true; + player->m_Local.m_flDucktime = 1000; + player->m_Local.m_bDucking = true; + } + + // no ducking or unducking while deployed or prone + if( m_pDODPlayer->m_Shared.IsProne() || + m_pDODPlayer->m_Shared.IsGettingUpFromProne() || + !m_pDODPlayer->m_Shared.CanChangePosition() ) + { + return; + } + + HandleDuckingSpeedCrop(); + + if ( !( player->GetFlags() & FL_DUCKING ) && ( player->m_Local.m_bDucked ) ) + { + player->m_Local.m_bDucked = false; + } + + /* + Msg( "duck button %s ducking %s ducked %s duck flags %s\n", + ( mv->m_nButtons & IN_DUCK ) ? "down" : "up", + ( player->m_Local.m_bDucking ) ? "yes" : "no", + ( player->m_Local.m_bDucked ) ? "yes" : "no", + ( player->GetFlags() & FL_DUCKING ) ? "set" : "not set" );*/ + + // Holding duck, in process of ducking or fully ducked? + if ( ( mv->m_nButtons & IN_DUCK ) || ( player->m_Local.m_bDucking ) || ( player->GetFlags() & FL_DUCKING ) ) + { + if ( mv->m_nButtons & IN_DUCK ) + { + bool alreadyDucked = ( player->GetFlags() & FL_DUCKING ) ? true : false; + + if ( (buttonsPressed & IN_DUCK ) && !( player->GetFlags() & FL_DUCKING ) ) + { + // Use 1 second so super long jump will work + player->m_Local.m_flDucktime = 1000; + player->m_Local.m_bDucking = true; + } + + float duckmilliseconds = MAX( 0.0f, 1000.0f - (float)player->m_Local.m_flDucktime ); + float duckseconds = duckmilliseconds / 1000.0f; + + //time = MAX( 0.0, ( 1.0 - (float)player->m_Local.m_flDucktime / 1000.0 ) ); + + if ( player->m_Local.m_bDucking ) + { + // Finish ducking immediately if duck time is over or not on ground + if ( ( duckseconds > TIME_TO_DUCK ) || + ( player->GetGroundEntity() == NULL ) || + alreadyDucked) + { + FinishDuck(); + } + else + { + // Calc parametric time + float flDuckFraction = SimpleSpline( duckseconds / TIME_TO_DUCK ); + SetDODDuckedEyeOffset( flDuckFraction ); + } + } + } + else + { + // Try to unduck unless automovement is not allowed + // NOTE: When not onground, you can always unduck + if ( player->m_Local.m_bAllowAutoMovement || player->GetGroundEntity() == NULL ) + { + if ( (buttonsReleased & IN_DUCK ) && ( player->GetFlags() & FL_DUCKING ) ) + { + // Use 1 second so super long jump will work + player->m_Local.m_flDucktime = 1000; + player->m_Local.m_bDucking = true; // or unducking + } + + float duckmilliseconds = MAX( 0.0f, 1000.0f - (float)player->m_Local.m_flDucktime ); + float duckseconds = duckmilliseconds / 1000.0f; + + if ( CanUnduck() ) + { + if ( player->m_Local.m_bDucking || + player->m_Local.m_bDucked ) // or unducking + { + // Finish ducking immediately if duck time is over or not on ground + if ( ( duckseconds > TIME_TO_UNDUCK ) || + ( player->GetGroundEntity() == NULL ) ) + { + FinishUnDuck(); + } + else + { + // Calc parametric time + float duckFraction = SimpleSpline( 1.0f - ( duckseconds / TIME_TO_UNDUCK ) ); + SetDODDuckedEyeOffset( duckFraction ); + } + } + } + else + { + // Still under something where we can't unduck, so make sure we reset this timer so + // that we'll unduck once we exit the tunnel, etc. + player->m_Local.m_flDucktime = 1000; + } + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : const Vector +//----------------------------------------------------------------------------- +Vector CDODGameMovement::GetPlayerMins( void ) const +{ + if ( !player ) + { + return vec3_origin; + } + + if ( player->IsObserver() ) + { + return VEC_OBS_HULL_MIN_SCALED( player ); + } + else + { + if ( player->m_Local.m_bDucked ) + return VEC_DUCK_HULL_MIN_SCALED( player ); + else if ( m_pDODPlayer->m_Shared.IsProne() ) + return VEC_PRONE_HULL_MIN_SCALED( player ); + else + return VEC_HULL_MIN_SCALED( player ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : const Vector +//----------------------------------------------------------------------------- +Vector CDODGameMovement::GetPlayerMaxs( void ) const +{ + if ( !player ) + { + return vec3_origin; + } + if ( player->IsObserver() ) + { + return VEC_OBS_HULL_MAX_SCALED( player ); + } + else + { + if ( player->m_Local.m_bDucked ) + return VEC_DUCK_HULL_MAX_SCALED( player ); + else if ( m_pDODPlayer->m_Shared.IsProne() ) + return VEC_PRONE_HULL_MAX_SCALED( player ); + else + return VEC_HULL_MAX_SCALED( player ); + } +} + +void CDODGameMovement::SetViewOffset( Vector vecViewOffset ) +{ + // call this instead of player->SetViewOffset directly, so we can cancel any + // animation in progress + + m_pDODPlayer->m_Shared.ResetViewOffsetAnimation(); + + player->SetViewOffset( vecViewOffset ); +} + +void CDODGameMovement::ViewOffsetAnimation( Vector vecDest, float flTime, ViewAnimationType type ) +{ + m_pDODPlayer->m_Shared.ViewOffsetAnimation( vecDest, flTime, type ); +}
\ No newline at end of file diff --git a/game/shared/dod/dod_gamerules.cpp b/game/shared/dod/dod_gamerules.cpp new file mode 100644 index 0000000..9a9e48d --- /dev/null +++ b/game/shared/dod/dod_gamerules.cpp @@ -0,0 +1,5595 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: The TF Game rules +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "dod_gamerules.h" +#include "ammodef.h" +#include "KeyValues.h" +#include "weapon_dodbase.h" +#include "filesystem.h" // for WriteStatsFile + + +#ifdef CLIENT_DLL + + #include "precache_register.h" + #include "c_dod_player.h" + +#else + + #include "coordsize.h" + #include "dod_player.h" + #include "voice_gamemgr.h" + #include "team.h" + #include "dod_player.h" + #include "dod_bot_temp.h" + #include "game.h" + #include "dod_shareddefs.h" + #include "player_resource.h" + #include "mapentities.h" + #include "dod_gameinterface.h" + #include "dod_objective_resource.h" + #include "dod_cvars.h" + #include "dod_team.h" + #include "dod_playerclass_info_parse.h" + #include "dod_control_point_master.h" + #include "dod_bombtarget.h" + //#include "teamplayroundbased_gamerules.h" + #include "weapon_dodbipodgun.h" + +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#ifndef CLIENT_DLL + +BEGIN_DATADESC(CSpawnPoint) + + // Keyfields + DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + +END_DATADESC(); + +LINK_ENTITY_TO_CLASS(info_player_allies, CSpawnPoint); +LINK_ENTITY_TO_CLASS(info_player_axis, CSpawnPoint); + +#endif + +REGISTER_GAMERULES_CLASS( CDODGameRules ); + +#define MAX_RESPAWN_WAVES_TO_TRANSMIT 5 + +#ifdef CLIENT_DLL +void RecvProxy_RoundState( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + CDODGameRules *pGamerules = ( CDODGameRules *)pStruct; + + int iRoundState = pData->m_Value.m_Int; + + pGamerules->SetRoundState( iRoundState ); +} +#endif + +BEGIN_NETWORK_TABLE_NOBASE( CDODGameRules, DT_DODGameRules ) + #ifdef CLIENT_DLL + + RecvPropInt( RECVINFO( m_iRoundState ), 0, RecvProxy_RoundState ), + RecvPropBool( RECVINFO( m_bInWarmup ) ), + RecvPropBool( RECVINFO( m_bAwaitingReadyRestart ) ), + RecvPropTime( RECVINFO( m_flMapResetTime ) ), + RecvPropTime( RECVINFO( m_flRestartRoundTime ) ), + RecvPropBool( RECVINFO( m_bAlliesAreBombing ) ), + RecvPropBool( RECVINFO( m_bAxisAreBombing ) ), + + RecvPropArray3( RECVINFO_ARRAY(m_AlliesRespawnQueue), RecvPropTime( RECVINFO(m_AlliesRespawnQueue[0]) ) ), + RecvPropArray3( RECVINFO_ARRAY(m_AxisRespawnQueue), RecvPropTime( RECVINFO(m_AxisRespawnQueue[0]) ) ), + RecvPropInt( RECVINFO( m_iAlliesRespawnHead ) ), + RecvPropInt( RECVINFO( m_iAlliesRespawnTail ) ), + RecvPropInt( RECVINFO( m_iAxisRespawnHead ) ), + RecvPropInt( RECVINFO( m_iAxisRespawnTail ) ), + + #else + + SendPropInt( SENDINFO( m_iRoundState ), 5 ), + SendPropBool( SENDINFO( m_bInWarmup ) ), + SendPropBool( SENDINFO( m_bAwaitingReadyRestart ) ), + SendPropTime( SENDINFO( m_flMapResetTime ) ), + SendPropTime( SENDINFO( m_flRestartRoundTime ) ), + SendPropBool( SENDINFO( m_bAlliesAreBombing ) ), + SendPropBool( SENDINFO( m_bAxisAreBombing ) ), + + SendPropArray3( SENDINFO_ARRAY3(m_AlliesRespawnQueue), SendPropTime( SENDINFO_ARRAY(m_AlliesRespawnQueue) ) ), + SendPropArray3( SENDINFO_ARRAY3(m_AxisRespawnQueue), SendPropTime( SENDINFO_ARRAY(m_AxisRespawnQueue) ) ), + SendPropInt( SENDINFO( m_iAlliesRespawnHead ), Q_log2( DOD_RESPAWN_QUEUE_SIZE ) + 1, SPROP_UNSIGNED ), + SendPropInt( SENDINFO( m_iAlliesRespawnTail ), Q_log2( DOD_RESPAWN_QUEUE_SIZE ) + 1, SPROP_UNSIGNED ), + SendPropInt( SENDINFO( m_iAxisRespawnHead ), Q_log2( DOD_RESPAWN_QUEUE_SIZE ) + 1, SPROP_UNSIGNED ), + SendPropInt( SENDINFO( m_iAxisRespawnTail ), Q_log2( DOD_RESPAWN_QUEUE_SIZE ) + 1, SPROP_UNSIGNED ), + + #endif +END_NETWORK_TABLE() + +#ifndef CLIENT_DLL +ConVar dod_flagrespawnbonus( "dod_flagrespawnbonus", "1.0", FCVAR_GAMEDLL | FCVAR_CHEAT, "How many seconds per advantage flag to decrease the respawn time" ); + +ConVar mp_warmup_time( "mp_warmup_time", "0", FCVAR_GAMEDLL, "Warmup time length in seconds" ); +ConVar mp_restartwarmup( "mp_restartwarmup", "0", FCVAR_GAMEDLL, "Set to 1 to start or restart the warmup period." ); +ConVar mp_cancelwarmup( "mp_cancelwarmup", "0", FCVAR_GAMEDLL, "Set to 1 to end the warmup period." ); +#endif + +ConVar dod_enableroundwaittime( "dod_enableroundwaittime", "1", FCVAR_REPLICATED, "Enable timers to wait between rounds." ); +ConVar mp_allowrandomclass( "mp_allowrandomclass", "1", FCVAR_REPLICATED, "Allow players to select random class" ); + +ConVar dod_bonusroundtime( "dod_bonusroundtime", "15", FCVAR_REPLICATED, "Time after round win until round restarts", true, 5, true, 15 ); + +LINK_ENTITY_TO_CLASS( dod_gamerules, CDODGameRulesProxy ); +IMPLEMENT_NETWORKCLASS_ALIASED( DODGameRulesProxy, DT_DODGameRulesProxy ) + + +#ifdef CLIENT_DLL + void RecvProxy_DODGameRules( const RecvProp *pProp, void **pOut, void *pData, int objectID ) + { + CDODGameRules *pRules = DODGameRules(); + Assert( pRules ); + *pOut = pRules; + } + + BEGIN_RECV_TABLE( CDODGameRulesProxy, DT_DODGameRulesProxy ) + RecvPropDataTable( "dod_gamerules_data", 0, 0, &REFERENCE_RECV_TABLE( DT_DODGameRules ), RecvProxy_DODGameRules ) + END_RECV_TABLE() +#else + void* SendProxy_DODGameRules( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID ) + { + CDODGameRules *pRules = DODGameRules(); + Assert( pRules ); + pRecipients->SetAllRecipients(); + return pRules; + } + + BEGIN_SEND_TABLE( CDODGameRulesProxy, DT_DODGameRulesProxy ) + SendPropDataTable( "dod_gamerules_data", 0, &REFERENCE_SEND_TABLE( DT_DODGameRules ), SendProxy_DODGameRules ) + END_SEND_TABLE() +#endif + +static CDODViewVectors g_DODViewVectors( + + Vector( 0, 0, 58 ), //VEC_VIEW (m_vView) + + Vector(-16, -16, 0 ), //VEC_HULL_MIN (m_vHullMin) + Vector( 16, 16, 72 ), //VEC_HULL_MAX (m_vHullMax) + + Vector(-16, -16, 0 ), //VEC_DUCK_HULL_MIN (m_vDuckHullMin) + Vector( 16, 16, 45 ), //VEC_DUCK_HULL_MAX (m_vDuckHullMax) + Vector( 0, 0, 34 ), //VEC_DUCK_VIEW (m_vDuckView) + + Vector(-10, -10, -10 ), //VEC_OBS_HULL_MIN (m_vObsHullMin) + Vector( 10, 10, 10 ), //VEC_OBS_HULL_MAX (m_vObsHullMax) + + Vector( 0, 0, 14 ), //VEC_DEAD_VIEWHEIGHT (m_vDeadViewHeight) + + Vector(-16, -16, 0 ), //VEC_PRONE_HULL_MIN (m_vProneHullMin) + Vector( 16, 16, 24 ) //VEC_PRONE_HULL_MAX (m_vProneHullMax) +); + + +#ifdef CLIENT_DLL + + +#else + + void ParseEntKVBlock( CBaseEntity *pNode, KeyValues *pkvNode ) + { + KeyValues *pkvNodeData = pkvNode->GetFirstSubKey(); + while ( pkvNodeData ) + { + // Handle the connections block + if ( !Q_strcmp(pkvNodeData->GetName(), "connections") ) + { + ParseEntKVBlock( pNode, pkvNodeData ); + } + else + { + pNode->KeyValue( pkvNodeData->GetName(), pkvNodeData->GetString() ); + } + + pkvNodeData = pkvNodeData->GetNextKey(); + } + } + + CUtlVector<EHANDLE> m_hSpawnedEntities; + + // for now only allow blocker walls to load this way + bool CanLoadEntityFromEntText( const char *clsName ) + { + if ( !Q_strcmp( clsName, "func_team_wall" ) ) + { + return true; + } + + return false; + } + + void Load_EntText( void ) + { + bool oldLock = engine->LockNetworkStringTables( false ); + + // remove all ents in m_SpawnedEntities + for ( int i = 0; i < m_hSpawnedEntities.Count(); i++ ) + { + UTIL_Remove( m_hSpawnedEntities[i] ); + } + + // delete the items from our list + m_hSpawnedEntities.RemoveAll(); + + // Find the commentary file + char szFullName[512]; + Q_snprintf(szFullName,sizeof(szFullName), "maps/%s.ent", STRING( gpGlobals->mapname )); + KeyValues *pkvFile = new KeyValues( "EntText" ); + if ( pkvFile->LoadFromFile( filesystem, szFullName, "MOD" ) ) + { + DevMsg( "Load_EntText: Loading entity data from %s. \n", szFullName ); + + // Load each commentary block, and spawn the entities + KeyValues *pkvNode = pkvFile->GetFirstSubKey(); + while ( pkvNode ) + { + // Get node name + const char *pNodeName = pkvNode->GetName(); + KeyValues *pClassname = pkvNode->FindKey( "classname" ); + if ( pClassname ) + { + // Use the classname instead + pNodeName = pClassname->GetString(); + } + + if ( CanLoadEntityFromEntText( pNodeName ) ) + { + // Spawn the entity + CBaseEntity *pNode = CreateEntityByName( pNodeName ); + if ( pNode ) + { + ParseEntKVBlock( pNode, pkvNode ); + DispatchSpawn( pNode ); + + EHANDLE hHandle; + hHandle = pNode; + m_hSpawnedEntities.AddToTail( hHandle ); + } + else + { + Warning("Load_EntText: Failed to spawn entity, type: '%s'\n", pNodeName ); + } + } + + // Move to next entity + pkvNode = pkvNode->GetNextKey(); + } + + // Then activate all the entities + for ( int i = 0; i < m_hSpawnedEntities.Count(); i++ ) + { + m_hSpawnedEntities[i]->Activate(); + } + } + else + { + DevMsg( "Load_EntText: Could not find entity data file '%s'. \n", szFullName ); + } + + engine->LockNetworkStringTables( oldLock ); + } + + // --------------------------------------------------------------------------------------------------- // + // Voice helper + // --------------------------------------------------------------------------------------------------- // + + class CVoiceGameMgrHelper : public IVoiceGameMgrHelper + { + public: + virtual bool CanPlayerHearPlayer( CBasePlayer *pListener, CBasePlayer *pTalker, bool &bProximity ) + { + // Dead players can only be heard by other dead team mates + if ( pTalker->IsAlive() == false ) + { + if ( pListener->IsAlive() == false ) + return ( pListener->InSameTeam( pTalker ) ); + + return false; + } + + return ( pListener->InSameTeam( pTalker ) ); + } + }; + CVoiceGameMgrHelper g_VoiceGameMgrHelper; + IVoiceGameMgrHelper *g_pVoiceGameMgrHelper = &g_VoiceGameMgrHelper; + + + + // --------------------------------------------------------------------------------------------------- // + // Globals. + // --------------------------------------------------------------------------------------------------- // + + // NOTE: the indices here must match TEAM_TERRORIST, TEAM_ALLIES, TEAM_AXIS, etc. + char *sTeamNames[] = + { + "Unassigned", + "Spectator", + "Allies", + "Axis" + }; + + static const char *s_PreserveEnts[] = + { + "player", + "viewmodel", + "worldspawn", + "soundent", + "ai_network", + "ai_hint", + "dod_gamerules", + "dod_team_manager", + "dod_player_manager", + "dod_objective_resource", + "env_soundscape", + "env_soundscape_proxy", + "env_soundscape_triggerable", + "env_sprite", + "env_sun", + "env_wind", + "env_fog_controller", + "func_brush", + "func_wall", + "func_illusionary", + "info_node", + "info_target", + "info_node_hint", + "info_player_allies", + "info_player_axis", + "point_viewcontrol", + "shadow_control", + "sky_camera", + "scene_manager", + "trigger_soundscape", + "info_dod_detect", + "dod_team_allies", + "dod_team_axis", + "point_commentary_node", + "dod_round_timer", + "func_precipitation", + "func_team_wall", + "", // END Marker + }; + + // --------------------------------------------------------------------------------------------------- // + // Global helper functions. + // --------------------------------------------------------------------------------------------------- // + + // World.cpp calls this but we don't use it in DoD. + void InitBodyQue() + { + } + + + // Handler for the "bot" command. + CON_COMMAND_F( bot, "Add a bot.", FCVAR_CHEAT ) + { + //CDODPlayer *pPlayer = CDODPlayer::Instance( UTIL_GetCommandClientIndex() ); + + // The bot command uses switches like command-line switches. + // -count <count> tells how many bots to spawn. + // -team <index> selects the bot's team. Default is -1 which chooses randomly. + // Note: if you do -team !, then it + // -class <index> selects the bot's class. Default is -1 which chooses randomly. + // -frozen prevents the bots from running around when they spawn in. + + // Look at -count. + int count = args.FindArgInt( "-count", 1 ); + count = clamp( count, 1, 16 ); + + int iTeam = TEAM_ALLIES; + const char *pVal = args.FindArg( "-team" ); + if ( pVal ) + { + iTeam = atoi( pVal ); + iTeam = clamp( iTeam, 0, (GetNumberOfTeams()-1) ); + } + + int iClass = 0; + pVal = args.FindArg( "-class" ); + if ( pVal ) + { + iClass = atoi( pVal ); + iClass = clamp( iClass, 0, 10 ); + } + + // Look at -frozen. + bool bFrozen = !!args.FindArg( "-frozen" ); + + // Ok, spawn all the bots. + while ( --count >= 0 ) + { + BotPutInServer( bFrozen, iTeam, iClass ); + } + } + + + void RestartRound_f() + { + DODGameRules()->State_Transition( STATE_RESTART ); + } + ConCommand cc_Restart( "restartround", RestartRound_f, "Restart the round", FCVAR_CHEAT ); + + void CDODGameRules::CopyGamePlayLogic( const CDODGamePlayRules otherGamePlay ) + { + m_GamePlayRules.CopyFrom( otherGamePlay ); + } + + // --------------------------------------------------------------------------------------------------- // + // CDODGameRules implementation. + // --------------------------------------------------------------------------------------------------- // + + CDODGameRules::CDODGameRules() + { + InitTeams(); + + ResetMapTime(); + + m_GamePlayRules.Reset(); + + ResetScores(); + + m_bInWarmup = false; + m_bAwaitingReadyRestart = false; + m_flRestartRoundTime = -1; + + m_iAlliesRespawnHead = 0; + m_iAlliesRespawnTail = 0; + m_iAxisRespawnHead = 0; + m_iAxisRespawnTail = 0; + m_iNumAlliesRespawnWaves = 0; + m_iNumAxisRespawnWaves = 0; + + for ( int i=0; i <DOD_RESPAWN_QUEUE_SIZE; i++ ) + { + m_AlliesRespawnQueue.Set( i, 0 ); + m_AxisRespawnQueue.Set( i, 0 ); + } + + m_bLevelInitialized = false; + m_iSpawnPointCount_Allies = 0; + m_iSpawnPointCount_Axis = 0; + + Q_memset( m_vecPlayerPositions,0, sizeof(m_vecPlayerPositions) ); + + // Lets execute a map specific cfg file + // Matt - execute this after server.cfg! + char szCommand[256] = { 0 }; + // Map names cannot contain quotes or control characters so this is safe but silly that we have to do it. + Q_snprintf( szCommand, sizeof(szCommand), "exec \"%s.cfg\"\n", STRING(gpGlobals->mapname) ); + engine->ServerCommand( szCommand ); + + m_pCurStateInfo = NULL; + State_Transition( STATE_PREGAME ); + + // stats + memset( m_iStatsKillsPerClass_Allies, 0, sizeof(m_iStatsKillsPerClass_Allies) ); + memset( m_iStatsKillsPerClass_Axis, 0, sizeof(m_iStatsKillsPerClass_Axis) ); + + memset( m_iStatsSpawnsPerClass_Allies, 0, sizeof(m_iStatsSpawnsPerClass_Allies) ); + memset( m_iStatsSpawnsPerClass_Axis, 0, sizeof(m_iStatsSpawnsPerClass_Axis) ); + + memset( m_iStatsCapsPerClass_Allies, 0, sizeof(m_iStatsCapsPerClass_Allies) ); + memset( m_iStatsCapsPerClass_Axis, 0, sizeof(m_iStatsCapsPerClass_Axis) ); + + memset( m_iStatsDefensesPerClass_Allies, 0, sizeof(m_iStatsDefensesPerClass_Allies) ); + memset( m_iStatsDefensesPerClass_Axis, 0, sizeof(m_iStatsDefensesPerClass_Axis) ); + + memset( &m_iWeaponShotsFired, 0, sizeof(m_iWeaponShotsFired) ); + memset( &m_iWeaponShotsHit, 0, sizeof(m_iWeaponShotsHit) ); + memset( &m_iWeaponDistanceBuckets, 0, sizeof(m_iWeaponDistanceBuckets) ); + + memset( &m_flSecondsPlayedPerClass_Allies, 0, sizeof(m_flSecondsPlayedPerClass_Allies) ); + memset( &m_flSecondsPlayedPerClass_Axis, 0, sizeof(m_flSecondsPlayedPerClass_Axis) ); + + m_bUsingTimer = false; + m_pRoundTimer = NULL; // created on first round spawn that requires a timer + + m_bAlliesAreBombing = false; + m_bAxisAreBombing = false; + + m_flNextFailSafeWaveCheckTime = 0; + + // Init the holiday + int day = 0, month = 0, year = 0; + +#ifdef WIN32 + GetCurrentDate( &day, &month, &year ); +#elif POSIX + time_t now = time(NULL); + struct tm *tm = localtime( &now ); + + day = tm->tm_mday + 1; + month = tm->tm_mon; + year = tm->tm_year + 1900; +#endif + + if ( ( month == 12 && day >= 1 ) || ( month == 1 && day <= 2 ) ) + { + m_bWinterHolidayActive = true; + } + else + { + m_bWinterHolidayActive = false; + } + } + + //----------------------------------------------------------------------------- + // Purpose: + //----------------------------------------------------------------------------- + CDODGameRules::~CDODGameRules() + { + // Note, don't delete each team since they are in the gEntList and will + // automatically be deleted from there, instead. + g_Teams.Purge(); + } + + void CDODGameRules::LevelShutdown( void ) + { + UploadLevelStats(); + + BaseClass::LevelShutdown(); + } + + #define MY_USHRT_MAX 0xffff + #define MY_UCHAR_MAX 0xff + + void CDODGameRules::UploadLevelStats( void ) + { + if ( Q_strlen( STRING( gpGlobals->mapname ) ) > 0 ) + { + int i,j; + CDODTeam *pAllies = GetGlobalDODTeam( TEAM_ALLIES ); + CDODTeam *pAxis = GetGlobalDODTeam( TEAM_AXIS ); + + dod_gamestats_t stats; + memset( &stats, 0, sizeof(stats) ); + + // Header + stats.header.iVersion = DOD_STATS_BLOB_VERSION; + Q_strncpy( stats.header.szGameName, "dod", sizeof(stats.header.szGameName) ); + Q_strncpy( stats.header.szMapName, STRING( gpGlobals->mapname ), sizeof( stats.header.szMapName ) ); + + ConVar *hostip = cvar->FindVar( "hostip" ); + if ( hostip ) + { + int ip = hostip->GetInt(); + stats.header.ipAddr[0] = ip >> 24; + stats.header.ipAddr[1] = ( ip >> 16 ) & 0xff; + stats.header.ipAddr[2] = ( ip >> 8 ) & 0xff; + stats.header.ipAddr[3] = ( ip ) & 0xff; + } + + ConVar *hostport = cvar->FindVar( "hostip" ); + if ( hostport ) + { + stats.header.port = hostport->GetInt(); + } + + stats.header.serverid = 0; + + stats.iMinutesPlayed = clamp( (short)( gpGlobals->curtime / 60 ), 0, MY_USHRT_MAX ); + + // Team Scores + stats.iNumAlliesWins = clamp( pAllies->GetRoundsWon(), 0, MY_UCHAR_MAX ); + stats.iNumAxisWins = clamp( pAxis->GetRoundsWon(), 0, MY_UCHAR_MAX ); + + stats.iAlliesTickPoints = clamp( pAllies->GetScore(), 0, MY_USHRT_MAX ); + stats.iAxisTickPoints = clamp( pAxis->GetScore(), 0, MY_USHRT_MAX ); + + // Player Data + for ( i=1;i<=MAX_PLAYERS;i++ ) + { + CDODPlayer *pPlayer = ToDODPlayer( UTIL_PlayerByIndex( i ) ); + + if ( pPlayer ) + { + // Sum up the time played for players that are still connected. + // players who disconnected had their connect time added in ClientDisconnected + + // Tally the latest time for this player + pPlayer->TallyLatestTimePlayedPerClass( pPlayer->GetTeamNumber(), pPlayer->m_Shared.DesiredPlayerClass() ); + + for( j=0;j<7;j++ ) + { + m_flSecondsPlayedPerClass_Allies[j] += pPlayer->m_flTimePlayedPerClass_Allies[j]; + m_flSecondsPlayedPerClass_Axis[j] += pPlayer->m_flTimePlayedPerClass_Axis[j]; + } + } + } + + // convert to minutes + for( j=0;j<7;j++ ) + { + stats.iMinutesPlayedPerClass_Allies[j] = clamp( (short)( m_flSecondsPlayedPerClass_Allies[j] / 60 ), 0, MY_USHRT_MAX ); + stats.iMinutesPlayedPerClass_Axis[j] = clamp( (short)( m_flSecondsPlayedPerClass_Axis[j] / 60 ), 0, MY_USHRT_MAX ); + } + + for ( i=0;i<6;i++ ) + { + stats.iKillsPerClass_Allies[i] = clamp( (short)m_iStatsKillsPerClass_Allies[i], 0, MY_USHRT_MAX ); + stats.iKillsPerClass_Axis[i] = clamp( (short)m_iStatsKillsPerClass_Axis[i], 0, MY_USHRT_MAX ); + + stats.iSpawnsPerClass_Allies[i] = clamp( (short)m_iStatsSpawnsPerClass_Allies[i], 0, MY_USHRT_MAX ); + stats.iSpawnsPerClass_Axis[i] = clamp( (short)m_iStatsSpawnsPerClass_Axis[i], 0, MY_USHRT_MAX ); + + stats.iCapsPerClass_Allies[i] = clamp( (short)m_iStatsCapsPerClass_Allies[i], 0, MY_USHRT_MAX ); + stats.iCapsPerClass_Axis[i] = clamp( (short)m_iStatsCapsPerClass_Axis[i], 0, MY_USHRT_MAX ); + + stats.iDefensesPerClass_Allies[i] = clamp( m_iStatsDefensesPerClass_Allies[i], 0, MY_UCHAR_MAX ); + stats.iDefensesPerClass_Axis[i] = clamp( m_iStatsDefensesPerClass_Axis[i], 0, MY_UCHAR_MAX ); + } + + // Server Settings + stats.iClassLimits_Allies[0] = clamp( mp_limitAlliesRifleman.GetInt(), -1, 254 ); + stats.iClassLimits_Allies[1] = clamp( mp_limitAlliesAssault.GetInt(), -1, 254 ); + stats.iClassLimits_Allies[2] = clamp( mp_limitAlliesSupport.GetInt(), -1, 254 ); + stats.iClassLimits_Allies[3] = clamp( mp_limitAlliesSniper.GetInt(), -1, 254 ); + stats.iClassLimits_Allies[4] = clamp( mp_limitAlliesMachinegun.GetInt(), -1, 254 ); + stats.iClassLimits_Allies[5] = clamp( mp_limitAlliesRocket.GetInt(), -1, 254 ); + + stats.iClassLimits_Axis[0] = clamp( mp_limitAxisRifleman.GetInt(), -1, 254 ); + stats.iClassLimits_Axis[1] = clamp( mp_limitAxisAssault.GetInt(), -1, 254 ); + stats.iClassLimits_Axis[2] = clamp( mp_limitAxisSupport.GetInt(), -1, 254 ); + stats.iClassLimits_Axis[3] = clamp( mp_limitAxisSniper.GetInt(), -1, 254 ); + stats.iClassLimits_Axis[4] = clamp( mp_limitAxisMachinegun.GetInt(), -1, 254 ); + stats.iClassLimits_Axis[5] = clamp( mp_limitAxisRocket.GetInt(), -1, 254 ); + + // Weapon Data + + // Send hit/shots/distance info for the following guns / modes + for ( i=0;i<DOD_NUM_DISTANCE_STAT_WEAPONS;i++ ) + { + int weaponId = iDistanceStatWeapons[i]; + + stats.weaponStatsDistance[i].iNumHits = clamp( m_iWeaponShotsHit[weaponId], 0, MY_USHRT_MAX ); + stats.weaponStatsDistance[i].iNumAttacks = clamp( m_iWeaponShotsFired[weaponId], 0, MY_USHRT_MAX ); + for ( int j=0;j<DOD_NUM_WEAPON_DISTANCE_BUCKETS;j++ ) + { + stats.weaponStatsDistance[i].iDistanceBuckets[j] = clamp( m_iWeaponDistanceBuckets[weaponId][j], 0, MY_USHRT_MAX ); + } + } + + // Send hit/shots info for the following guns / modes + for ( i=0;i<DOD_NUM_NODIST_STAT_WEAPONS;i++ ) + { + int weaponId = iNoDistStatWeapons[i]; + stats.weaponStats[i].iNumHits = clamp( m_iWeaponShotsHit[weaponId], 0, MY_USHRT_MAX ); + stats.weaponStats[i].iNumAttacks = clamp( m_iWeaponShotsFired[weaponId], 0, MY_USHRT_MAX ); + } + + const void *pvBlobData = ( const void * )( &stats ); + unsigned int uBlobSize = sizeof( stats ); + + if ( gamestatsuploader ) + { + gamestatsuploader->UploadGameStats( + STRING( gpGlobals->mapname ), + DOD_STATS_BLOB_VERSION, + uBlobSize, + pvBlobData ); + } + } + } + + void CDODGameRules::Stats_PlayerKill( int team, int cls ) + { + Assert( cls >= 0 && cls <= 5 ); + + if ( cls >= 0 && cls <= 5 ) + { + if ( team == TEAM_ALLIES ) + m_iStatsKillsPerClass_Allies[cls]++; + else if ( team == TEAM_AXIS ) + m_iStatsKillsPerClass_Axis[cls]++; + } + } + + void CDODGameRules::Stats_PlayerCap( int team, int cls ) + { + Assert( cls >= 0 && cls <= 5 ); + + if ( cls >= 0 && cls <= 5 ) + { + if ( team == TEAM_ALLIES ) + m_iStatsCapsPerClass_Allies[cls]++; + else if ( team == TEAM_AXIS ) + m_iStatsCapsPerClass_Axis[cls]++; + } + } + + void CDODGameRules::Stats_PlayerDefended( int team, int cls ) + { + Assert( cls >= 0 && cls <= 5 ); + + if ( cls >= 0 && cls <= 5 ) + { + if ( team == TEAM_ALLIES ) + m_iStatsDefensesPerClass_Allies[cls]++; + else if ( team == TEAM_AXIS ) + m_iStatsDefensesPerClass_Axis[cls]++; + } + } + + void CDODGameRules::Stats_WeaponFired( int weaponID ) + { + m_iWeaponShotsFired[weaponID]++; + } + + void CDODGameRules::Stats_WeaponHit( int weaponID, float flDist ) + { + m_iWeaponShotsHit[weaponID]++; + + int bucket = Stats_WeaponDistanceToBucket( weaponID, flDist ); + m_iWeaponDistanceBuckets[weaponID][bucket]++; + } + + int CDODGameRules::Stats_WeaponDistanceToBucket( int weaponID, float flDist ) + { + int bucket = 4; + int iDist = (int)flDist; + + for ( int i=0;i<DOD_NUM_WEAPON_DISTANCE_BUCKETS-1;i++ ) + { + if ( iDist < iWeaponBucketDistances[i] ) + { + bucket = i; + break; + } + } + + return bucket; + } + + + //----------------------------------------------------------------------------- + // Purpose: DoD Specific Client Commands + // Input : + // Output : + //----------------------------------------------------------------------------- + bool CDODGameRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args ) + { + CDODPlayer *pPlayer = ToDODPlayer( pEdict ); + const char *pcmd = args[0]; +#ifdef DEBUG + if ( FStrEq( pcmd, "teamwin" ) ) + { + if ( args.ArgC() < 2 ) + return true; + + SetWinningTeam( atoi( args[1] ) ); + + return true; + } + else +#endif + // Handle some player commands here as they relate more directly to gamerules state + if ( FStrEq( pcmd, "nextmap" ) ) + { + CDODPlayer *pDODPlayer = ToDODPlayer(pPlayer); + + if ( pDODPlayer->m_flNextTimeCheck < gpGlobals->curtime ) + { + char szNextMap[32]; + + if ( nextlevel.GetString() && *nextlevel.GetString() ) + { + Q_strncpy( szNextMap, nextlevel.GetString(), sizeof( szNextMap ) ); + } + else + { + GetNextLevelName( szNextMap, sizeof( szNextMap ) ); + } + + ClientPrint( pPlayer, HUD_PRINTTALK, "#game_nextmap", szNextMap); + + pDODPlayer->m_flNextTimeCheck = gpGlobals->curtime + 1; + } + + return true; + } + else if ( FStrEq( pcmd, "timeleft" ) ) + { + CDODPlayer *pDODPlayer = ToDODPlayer(pPlayer); + + if ( pDODPlayer->m_flNextTimeCheck < gpGlobals->curtime ) + { + if ( mp_timelimit.GetInt() > 0 ) + { + int iTimeLeft = GetTimeLeft(); + + char szMinutes[5]; + char szSeconds[3]; + + if ( iTimeLeft <= 0 ) + { + Q_snprintf( szMinutes, sizeof(szMinutes), "0" ); + Q_snprintf( szSeconds, sizeof(szSeconds), "00" ); + } + else + { + Q_snprintf( szMinutes, sizeof(szMinutes), "%d", iTimeLeft / 60 ); + Q_snprintf( szSeconds, sizeof(szSeconds), "%02d", iTimeLeft % 60 ); + } + + ClientPrint( pPlayer, HUD_PRINTTALK, "#game_time_left1", szMinutes, szSeconds ); + } + else + { + ClientPrint( pPlayer, HUD_PRINTTALK, "#game_time_left2" ); + } + + CDODPlayer *pDODPlayer = ToDODPlayer(pPlayer); + pDODPlayer->m_flNextTimeCheck = gpGlobals->curtime + 1; + } + return true; + } + else if ( pPlayer->ClientCommand( args ) ) + { + return true; + } + else if ( BaseClass::ClientCommand( pEdict, args ) ) + { + return true; + } + + return false; + } + + void CDODGameRules::CheckChatForReadySignal( CDODPlayer *pPlayer, const char *chatmsg ) + { + if( m_bAwaitingReadyRestart && FStrEq( chatmsg, mp_clan_ready_signal.GetString() ) ) + { + if( !m_bHeardAlliesReady && pPlayer->GetTeamNumber() == TEAM_ALLIES ) + { + m_bHeardAlliesReady = true; + + IGameEvent *event = gameeventmanager->CreateEvent( "dod_allies_ready" ); + if ( event ) + gameeventmanager->FireEvent( event ); + } + else if( !m_bHeardAxisReady && pPlayer->GetTeamNumber() == TEAM_AXIS ) + { + m_bHeardAxisReady = true; + IGameEvent *event = gameeventmanager->CreateEvent( "dod_axis_ready" ); + if ( event ) + gameeventmanager->FireEvent( event ); + } + } + } + + int CDODGameRules::SelectDefaultTeam() + { + int team = TEAM_UNASSIGNED; + + CDODTeam *pAllies = GetGlobalDODTeam(TEAM_ALLIES); + CDODTeam *pAxis = GetGlobalDODTeam(TEAM_AXIS); + + int iNumAllies = pAllies->GetNumPlayers(); + int iNumAxis = pAxis->GetNumPlayers(); + + int iAlliesRoundsWon = pAllies->GetRoundsWon(); + int iAxisRoundsWon = pAxis->GetRoundsWon(); + + int iAlliesPoints = pAllies->GetScore(); + int iAxisPoints = pAxis->GetScore(); + + // Choose the team that's lacking players + if ( iNumAllies < iNumAxis ) + { + team = TEAM_ALLIES; + } + else if ( iNumAllies > iNumAxis ) + { + team = TEAM_AXIS; + } + // Choose the team that's losing + else if ( iAlliesRoundsWon < iAxisRoundsWon ) + { + team = TEAM_ALLIES; + } + else if ( iAlliesRoundsWon > iAxisRoundsWon ) + { + team = TEAM_AXIS; + } + // choose the team with fewer points + else if ( iAlliesPoints < iAxisPoints ) + { + team = TEAM_ALLIES; + } + else if ( iAlliesPoints > iAxisPoints ) + { + team = TEAM_AXIS; + } + else + { + // Teams and scores are equal, pick a random team + team = ( random->RandomInt(0,1) == 0 ) ? TEAM_ALLIES : TEAM_AXIS; + } + + if ( TeamFull( team ) ) + { + // Pick the opposite team + if ( team == TEAM_ALLIES ) + { + team = TEAM_AXIS; + } + else + { + team = TEAM_ALLIES; + } + + // No choices left + if ( TeamFull( team ) ) + return TEAM_UNASSIGNED; + } + + return team; + } + + bool CDODGameRules::TeamFull( int team_id ) + { + switch ( team_id ) + { + case TEAM_ALLIES: + { + int iNumAllies = GetGlobalDODTeam(TEAM_ALLIES)->GetNumPlayers(); + return iNumAllies >= m_iSpawnPointCount_Allies; + } + case TEAM_AXIS: + { + int iNumAxis = GetGlobalDODTeam(TEAM_AXIS)->GetNumPlayers(); + return iNumAxis >= m_iSpawnPointCount_Axis; + } + } + + return false; + } + + + //----------------------------------------------------------------------------- + // Purpose: Player has just spawned. Equip them. + //----------------------------------------------------------------------------- + + // return a multiplier that should adjust the damage done by a blast at position vecSrc to something at the position + // vecEnd. This will take into account the density of an entity that blocks the line of sight from one position to + // the other. + // + // this algorithm was taken from the HL2 version of RadiusDamage. + float CDODGameRules::GetExplosionDamageAdjustment(Vector & vecSrc, Vector & vecEnd, CBaseEntity *pTarget, CBaseEntity *pEntityToIgnore) + { + float retval = 0.0; + trace_t tr; + + UTIL_TraceLine(vecSrc, vecEnd, MASK_SHOT, pEntityToIgnore, COLLISION_GROUP_NONE, &tr); + + Assert( pTarget ); + + // its a hit if we made it to the dest, or if we hit another part of the target on the way + if (tr.fraction == 1.0 || tr.m_pEnt == pTarget ) + { + retval = 1.0; + } + else if (!(tr.DidHitWorld()) && (tr.m_pEnt != NULL) && (tr.m_pEnt->GetOwnerEntity() != pEntityToIgnore)) + { + // if we didn't hit world geometry perhaps there's still damage to be done here. + + CBaseEntity *blockingEntity = tr.m_pEnt; + + // check to see if this part of the player is visible if entities are ignored. + UTIL_TraceLine(vecSrc, vecEnd, CONTENTS_SOLID, NULL, COLLISION_GROUP_NONE, &tr); + + if (tr.fraction == 1.0) + { + if ((blockingEntity != NULL) && (blockingEntity->VPhysicsGetObject() != NULL)) + { + int nMaterialIndex = blockingEntity->VPhysicsGetObject()->GetMaterialIndex(); + + float flDensity; + float flThickness; + float flFriction; + float flElasticity; + + physprops->GetPhysicsProperties( nMaterialIndex, &flDensity, + &flThickness, &flFriction, &flElasticity ); + + const float ONE_OVER_DENSITY_ABSORB_ALL_DAMAGE = ( 1.0 / 3000.0 ); + float scale = flDensity * ONE_OVER_DENSITY_ABSORB_ALL_DAMAGE; + + if ((scale >= 0.0) && (scale < 1.0)) + { + retval = 1.0 - scale; + } + else if (scale < 0.0) + { + // should never happen, but just in case. + retval = 1.0; + } + } + else + { + retval = 0.75; // we're blocked by something that isn't an entity with a physics module or world geometry, just cut damage in half for now. + } + } + } + + return retval; + } + + // returns the percentage of the player that is visible from the given point in the world. + // return value is between 0 and 1. + float CDODGameRules::GetAmountOfEntityVisible(Vector & vecSrc, CBaseEntity *entity, CBaseEntity *pIgnoreEntity ) + { + float retval = 0.0; + + Vector vecHullSizeNormal = VEC_HULL_MAX - VEC_HULL_MIN; + + const float damagePercentageChest = 0.40; + const float damagePercentageHead = 0.30; + const float damagePercentageFoot = 0.10; // x 2 + const float damagePercentageHand = 0.05; // x 2 + + if (!(entity->IsPlayer())) + { + // the entity is not a player, so the damage is all or nothing. + Vector vecTarget; + vecTarget = entity->BodyTarget(vecSrc, false); + + return GetExplosionDamageAdjustment(vecSrc, vecTarget, entity, pIgnoreEntity); + } + + CDODPlayer *player = ToDODPlayer(entity); + + /* + new, sane method + */ + + static int iRHandIndex = 0; + static int iLHandIndex = 0; + static int iHeadIndex = 0; + static int iChestIndex = 0; + static int iRFootIndex = 0; + static int iLFootIndex = 0; + + static bool bInitializedBones = false; + + if ( !bInitializedBones ) + { + iRHandIndex = player->LookupBone( "ValveBiped.Bip01_R_Hand" ); + iLHandIndex = player->LookupBone( "ValveBiped.Bip01_L_Hand" ); + iHeadIndex = player->LookupBone( "ValveBiped.Bip01_Head1" ); + iChestIndex = player->LookupBone( "ValveBiped.Bip01_Spine2" ); + iRFootIndex = player->LookupBone( "ValveBiped.Bip01_R_Foot" ); + iLFootIndex = player->LookupBone( "ValveBiped.Bip01_L_Foot" ); + + Assert( iRHandIndex != -1 ); + Assert( iLHandIndex != -1 ); + Assert( iHeadIndex != -1 ); + Assert( iChestIndex != -1 ); + Assert( iRFootIndex != -1 ); + Assert( iLFootIndex != -1 ); + + bInitializedBones = true; + } + +#ifdef _DEBUG + // verify that these bone indeces don't change + int checkBoneIndex = player->LookupBone( "ValveBiped.Bip01_R_Hand" ); + Assert( checkBoneIndex == iRHandIndex ); +#endif + + + QAngle dummyAngle; + + Vector vecRHand; + player->GetBonePosition( iRHandIndex, vecRHand, dummyAngle ); + + Vector vecLHand; + player->GetBonePosition( iLHandIndex, vecLHand, dummyAngle ); + + Vector vecHead; + player->GetBonePosition( iHeadIndex, vecHead, dummyAngle ); + + Vector vecChest; + player->GetBonePosition( iChestIndex, vecChest, dummyAngle ); + + Vector vecRFoot; + player->GetBonePosition( iRFootIndex, vecRFoot, dummyAngle ); + + Vector vecLFoot; + player->GetBonePosition( iLFootIndex, vecLFoot, dummyAngle ); + + // right hand + float damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecRHand, player, pIgnoreEntity ); + retval += (damagePercentageHand * damageAdjustment); + +/* + Msg( "right hand: %.1f\n", damageAdjustment ); + NDebugOverlay::Line( vecSrc, vecRHand, + (int)(damageAdjustment * 255.0), + (int)((1.0 - damageAdjustment) * 255.0), + 0, + true, + 10 );*/ + + + // left hand + damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecLHand, player, pIgnoreEntity ); + retval += (damagePercentageHand * damageAdjustment); + +/* + Msg( "left hand: %.1f\n", damageAdjustment ); + NDebugOverlay::Line( vecSrc, vecLHand, + (int)(damageAdjustment * 255.0), + (int)((1.0 - damageAdjustment) * 255.0), + 0, + true, + 10 );*/ + + + // head + damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecHead, player, pIgnoreEntity ); + retval += (damagePercentageHead * damageAdjustment); + +/* + Msg( "head: %.1f\n", damageAdjustment ); + NDebugOverlay::Line( vecSrc, vecHead, + (int)(damageAdjustment * 255.0), + (int)((1.0 - damageAdjustment) * 255.0), + 0, + true, + 10 );*/ + + + // chest + damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecChest, player, pIgnoreEntity ); + retval += (damagePercentageChest * damageAdjustment); + +/* + Msg( "chest: %.1f\n", damageAdjustment ); + NDebugOverlay::Line( vecSrc, vecChest, + (int)(damageAdjustment * 255.0), + (int)((1.0 - damageAdjustment) * 255.0), + 0, + true, + 10 );*/ + + + // right foot + damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecRFoot, player, pIgnoreEntity ); + retval += (damagePercentageFoot * damageAdjustment); + +/* + Msg( "right foot: %.1f\n", damageAdjustment ); + NDebugOverlay::Line( vecSrc, vecRFoot, + (int)(damageAdjustment * 255.0), + (int)((1.0 - damageAdjustment) * 255.0), + 0, + true, + 10 );*/ + + + // left foot + damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecRFoot, player, pIgnoreEntity ); + retval += (damagePercentageFoot * damageAdjustment); + +/* + Msg( "left foot: %.1f\n", damageAdjustment ); + NDebugOverlay::Line( vecSrc, vecRFoot, + (int)(damageAdjustment * 255.0), + (int)((1.0 - damageAdjustment) * 255.0), + 0, + true, + 10 );*/ + + +// Msg( "total: %.1f\n", retval ); + + return retval; + } + + void CDODGameRules::RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore, CBaseEntity *pEntityIgnore ) + { + RadiusDamage( info, vecSrcIn, flRadius, iClassIgnore, pEntityIgnore, false ); + } + + ConVar r_visualizeExplosion( "r_visualizeExplosion", "0", FCVAR_CHEAT ); + + void CDODGameRules::RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore, CBaseEntity *pEntityIgnore, bool bIgnoreWorld /* = false */ ) + { + CBaseEntity *pEntity = NULL; + trace_t tr; + float flAdjustedDamage, falloff; + Vector vecSpot; + Vector vecToTarget; + + Vector vecSrc = vecSrcIn; + + float flDamagePercentage; + + if ( flRadius ) + falloff = info.GetDamage() / flRadius; + else + falloff = 1.0; + + vecSrc.z += 1;// in case grenade is lying on the ground + + if ( r_visualizeExplosion.GetBool() ) + { + float flLethalRange = ( info.GetDamage() - 100 ) / falloff; + float flHalfDamageRange = ( info.GetDamage() - 50 ) / falloff; + float flZeroDamageRange = ( info.GetDamage() ) / falloff; + + // draw a red sphere representing the kill area + Vector dest = vecSrc; + dest.x += flLethalRange; + + NDebugOverlay::HorzArrow( vecSrc, dest, 10, 255, 0, 0, 255, true, 10.0 ); + + // yellow for 50 damage + dest = vecSrc; + dest.x += flHalfDamageRange; + + NDebugOverlay::HorzArrow( vecSrc, dest, 10, 255, 255, 0, 255, true, 10.0 ); + + // green for > 0 damage + dest = vecSrc; + dest.x += flZeroDamageRange; + + NDebugOverlay::HorzArrow( vecSrc, dest, 10, 0, 255, 0, 255, true, 10.0 ); + } + + // iterate on all entities in the vicinity. + for ( CEntitySphereQuery sphere( vecSrc, flRadius ); ( pEntity = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() ) + { + if ( pEntity->m_takedamage != DAMAGE_NO ) + { + // UNDONE: this should check a damage mask, not an ignore + if ( iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore ) + continue; + + if ( pEntity == pEntityIgnore ) + continue; + + // radius damage can only be blocked by the world + vecSpot = pEntity->BodyTarget( vecSrc ); + + if ( bIgnoreWorld ) + { + flDamagePercentage = 1.0; + } + else + { + // get the percentage of the target entity that is visible from the + // explosion position. + flDamagePercentage = GetAmountOfEntityVisible(vecSrc, pEntity, info.GetInflictor() ); + } + + if (flDamagePercentage > 0.0) + { + // the explosion can 'see' this entity, so hurt them! + vecToTarget = ( vecSpot - vecSrc ); + + // decrease damage for an ent that's farther from the bomb. + flAdjustedDamage = vecToTarget.Length() * falloff; + flAdjustedDamage = info.GetDamage() - flAdjustedDamage; + + flAdjustedDamage *= flDamagePercentage; + + if ( flAdjustedDamage > 0 ) + { + CTakeDamageInfo adjustedInfo = info; + adjustedInfo.SetDamage( flAdjustedDamage ); + + Vector dir = vecToTarget; + VectorNormalize( dir ); + + // If we don't have a damage force, manufacture one + if ( adjustedInfo.GetDamagePosition() == vec3_origin || adjustedInfo.GetDamageForce() == vec3_origin ) + { + CalculateExplosiveDamageForce( &adjustedInfo, dir, vecSrc, 1.5 /* explosion scale! */ ); + } + else + { + // Assume the force passed in is the maximum force. Decay it based on falloff. + float flForce = adjustedInfo.GetDamageForce().Length() * falloff; + adjustedInfo.SetDamageForce( dir * flForce ); + adjustedInfo.SetDamagePosition( vecSrc ); + } + + pEntity->TakeDamage( adjustedInfo ); + + // Now hit all triggers along the way that respond to damage... + pEntity->TraceAttackToTriggers( adjustedInfo, vecSrc, vecSpot, dir ); + } + } + } + } + } + + void CDODGameRules::RadiusStun( const CTakeDamageInfo &info, const Vector &vecSrc, float flRadius ) + { + CBaseEntity *pEntity = NULL; + trace_t tr; + float flAdjustedDamage, falloff; + Vector vecSpot; + Vector vecToTarget; + + if ( flRadius ) + falloff = info.GetDamage() / flRadius; + else + falloff = 1.0; + + // ok, now send updates to all clients + CBitVec< ABSOLUTE_PLAYER_LIMIT > playerbits; + playerbits.ClearAll(); + + // see which players are actually in the PVS of the grenade + engine->Message_DetermineMulticastRecipients( false, vecSrc, playerbits ); + + // Iterate through all players that made it into playerbits, that are inside the radius + // and give them stun damage + for ( int i=0;i<MAX_PLAYERS;i++ ) + { + if ( playerbits.Get(i) == false ) + continue; + + pEntity = UTIL_EntityByIndex( i+1 ); + + if ( !pEntity || !pEntity->IsPlayer() ) + continue; + + if ( pEntity->m_takedamage != DAMAGE_NO ) + { + // radius damage can only be blocked by the world + vecSpot = pEntity->BodyTarget( vecSrc ); + + // the explosion can 'see' this entity, so hurt them! + vecToTarget = ( vecSpot - vecSrc ); + + float flDist = vecToTarget.Length(); + + // make sure they are inside the radius + if ( flDist > flRadius ) + continue; + + // decrease damage for an ent that's farther from the bomb. + flAdjustedDamage = flDist * falloff; + flAdjustedDamage = info.GetDamage() - flAdjustedDamage; + + if ( flAdjustedDamage > 0 ) + { + CTakeDamageInfo adjustedInfo = info; + adjustedInfo.SetDamage( flAdjustedDamage ); + + pEntity->TakeDamage( adjustedInfo ); + } + } + } + } + + void CDODGameRules::Think() + { + if ( g_fGameOver ) // someone else quit the game already + { + // check to see if we should change levels now + if ( m_flIntermissionEndTime < gpGlobals->curtime ) + { + ChangeLevel(); // intermission is over + } + + return; + } + + State_Think(); + + if ( gpGlobals->curtime > m_flNextPeriodicThink ) + { + if ( CheckTimeLimit() ) + return; + + if ( CheckWinLimit() ) + return; + + CheckRestartRound(); + CheckWarmup(); + CheckPlayerPositions(); + + m_flNextPeriodicThink = gpGlobals->curtime + 1.0; + } + + CGameRules::Think(); + } + + void CDODGameRules::GoToIntermission( void ) + { + BaseClass::GoToIntermission(); + + // set all players to FL_FROZEN + for ( int i = 1; i <= MAX_PLAYERS; i++ ) + { + CDODPlayer *pPlayer = ToDODPlayer( UTIL_PlayerByIndex( i ) ); + + if ( pPlayer ) + { + pPlayer->AddFlag( FL_FROZEN ); + + pPlayer->StatEvent_UploadStats(); + } + } + + // Print out map stats to a text file + //WriteStatsFile( "stats.xml" ); + + State_Enter( STATE_GAME_OVER ); + } + + void CDODGameRules::SetInWarmup( bool bWarmup ) + { + if( m_bInWarmup == bWarmup ) + return; + + m_bInWarmup = bWarmup; + + if( m_bInWarmup ) + { + m_flWarmupTimeEnds = gpGlobals->curtime + mp_warmup_time.GetFloat(); + DevMsg( "Warmup_Begin\n" ); + + IGameEvent *event = gameeventmanager->CreateEvent( "dod_warmup_begins" ); + if ( event ) + gameeventmanager->FireEvent( event ); + } + else + { + m_flWarmupTimeEnds = -1; + DevMsg( "Warmup_Ends\n" ); + + IGameEvent *event = gameeventmanager->CreateEvent( "dod_warmup_ends" ); + if ( event ) + gameeventmanager->FireEvent( event ); + } + } + + void CDODGameRules::CheckWarmup( void ) + { + if( mp_restartwarmup.GetBool() ) + { + if( m_bInWarmup ) + { + m_flWarmupTimeEnds = gpGlobals->curtime + mp_warmup_time.GetFloat(); + } + else + DODGameRules()->SetInWarmup( true ); + + mp_restartwarmup.SetValue( 0 ); + } + + if( mp_cancelwarmup.GetBool() ) + { + DODGameRules()->SetInWarmup( false ); + mp_cancelwarmup.SetValue( 0 ); + } + + if( m_bInWarmup ) + { + // only exit the warmup if the time is up, and we are not in a round + // restart countdown already, and we are not waiting for a ready restart + if( gpGlobals->curtime > m_flWarmupTimeEnds && m_flRestartRoundTime < 0 && !m_bAwaitingReadyRestart ) + { + // no need to end the warmup, the restart will end it automatically + //SetInWarmup( false ); + + m_flRestartRoundTime = gpGlobals->curtime; // reset asap + } + } + } + + void CDODGameRules::CheckRestartRound( void ) + { + if( mp_clan_readyrestart.GetBool() ) + { + m_bAwaitingReadyRestart = true; + m_bHeardAlliesReady = false; + m_bHeardAxisReady = false; + + const char *pszReadyString = mp_clan_ready_signal.GetString(); + + UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "#clan_ready_rules", pszReadyString ); + UTIL_ClientPrintAll( HUD_PRINTTALK, "#clan_ready_rules", pszReadyString ); + + // Don't let them put anything malicious in there + if( pszReadyString == NULL || Q_strlen(pszReadyString) > 16 ) + { + pszReadyString = "ready"; + } + + IGameEvent *event = gameeventmanager->CreateEvent( "dod_ready_restart" ); + if ( event ) + gameeventmanager->FireEvent( event ); + + mp_clan_readyrestart.SetValue( 0 ); + + // cancel any restart round in progress + m_flRestartRoundTime = -1; + } + + // Restart the game if specified by the server + int iRestartDelay = mp_clan_restartround.GetInt(); + + if ( iRestartDelay > 0 ) + { + if ( iRestartDelay > 60 ) + iRestartDelay = 60; + + m_flRestartRoundTime = gpGlobals->curtime + iRestartDelay; + + IGameEvent *event = gameeventmanager->CreateEvent( "dod_round_restart_seconds" ); + if ( event ) + { + event->SetInt( "seconds", iRestartDelay ); + gameeventmanager->FireEvent( event ); + } + + mp_clan_restartround.SetValue( 0 ); + + // cancel any ready restart in progress + m_bAwaitingReadyRestart = false; + } + } + + bool CDODGameRules::CheckTimeLimit() + { + if ( IsGameUnderTimeLimit() ) + { + if( GetTimeLeft() <= 0 ) + { + IGameEvent *event = gameeventmanager->CreateEvent( "dod_game_over" ); + if ( event ) + { + event->SetString( "reason", "Reached Time Limit" ); + gameeventmanager->FireEvent( event ); + } + + SendTeamScoresEvent(); + + GoToIntermission(); + return true; + } + } + + return false; + } + + bool CDODGameRules::CheckWinLimit() + { + // has one team won the specified number of rounds? + + int iWinLimit = mp_winlimit.GetInt(); + + if ( iWinLimit > 0 ) + { + CDODTeam *pAllies = GetGlobalDODTeam(TEAM_ALLIES); + CDODTeam *pAxis = GetGlobalDODTeam(TEAM_AXIS); + + bool bAlliesWin = pAllies->GetRoundsWon() >= iWinLimit; + bool bAxisWin = pAxis->GetRoundsWon() >= iWinLimit; + + if ( bAlliesWin || bAxisWin ) + { + IGameEvent *event = gameeventmanager->CreateEvent( "dod_game_over" ); + if ( event ) + { + event->SetString( "reason", "Reached Round Win Limit" ); + gameeventmanager->FireEvent( event ); + } + + GoToIntermission(); + return true; + } + } + + return false; + } + + void CDODGameRules::CheckPlayerPositions() + { + int i; + bool bUpdatePlayer[MAX_PLAYERS]; + Q_memset( bUpdatePlayer, 0, sizeof(bUpdatePlayer) ); + + // check all players + for ( i=1; i<=gpGlobals->maxClients; i++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + + if ( !pPlayer ) + continue; + + Vector origin = pPlayer->GetAbsOrigin(); + + Vector2D pos( (int)(origin.x/4), (int)(origin.y/4) ); + + if ( pos == m_vecPlayerPositions[i-1] ) + continue; // player didn't move enough + + m_vecPlayerPositions[i-1] = pos; + + bUpdatePlayer[i-1] = true; // player position changed since last time + } + + // ok, now send updates to all clients + CBitVec< ABSOLUTE_PLAYER_LIMIT > playerbits; + + for ( i=1; i<=gpGlobals->maxClients; i++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + + if ( !pPlayer ) + continue; + + if ( !pPlayer->IsConnected() ) + continue; + + CSingleUserRecipientFilter filter(pPlayer); + + UserMessageBegin( filter, "UpdateRadar" ); + + playerbits.ClearAll(); + + // see what other players are in it's PVS, don't update them + engine->Message_DetermineMulticastRecipients( false, pPlayer->EyePosition(), playerbits ); + + for ( int i=0; i < gpGlobals->maxClients; i++ ) + { + if ( playerbits.Get(i) ) + continue; // this player is in his PVS, don't update radar pos + + if ( !bUpdatePlayer[i] ) + continue; + + CBasePlayer *pOtherPlayer = UTIL_PlayerByIndex( i+1 ); + + if ( !pOtherPlayer ) + continue; // nothing there + + if ( pOtherPlayer == pPlayer ) + continue; // dont update himself + + if ( !pOtherPlayer->IsAlive() || pOtherPlayer->IsObserver() || !pOtherPlayer->IsConnected() ) + continue; // don't update spectators or dead players + + if ( pPlayer->GetTeamNumber() > TEAM_SPECTATOR ) + { + // update only team mates if not a pure spectator + if ( pPlayer->GetTeamNumber() != pOtherPlayer->GetTeamNumber() ) + continue; + } + + WRITE_BYTE( i+1 ); // player entity index + WRITE_SBITLONG( m_vecPlayerPositions[i].x, COORD_INTEGER_BITS-1 ); + WRITE_SBITLONG( m_vecPlayerPositions[i].y, COORD_INTEGER_BITS-1 ); + WRITE_SBITLONG( AngleNormalize( pOtherPlayer->GetAbsAngles().y ), 9 ); + } + + WRITE_BYTE( 0 ); // end marker + + MessageEnd(); // send message + } + } + + Vector DropToGround( + CBaseEntity *pMainEnt, + const Vector &vPos, + const Vector &vMins, + const Vector &vMaxs ) + { + trace_t trace; + UTIL_TraceHull( vPos, vPos + Vector( 0, 0, -500 ), vMins, vMaxs, MASK_SOLID, pMainEnt, COLLISION_GROUP_NONE, &trace ); + return trace.endpos; + } + + + void TestSpawnPointType( const char *pEntClassName ) + { + // Find the next spawn spot. + CBaseEntity *pSpot = gEntList.FindEntityByClassname( NULL, pEntClassName ); + + while( pSpot ) + { + // check if pSpot is valid + if( g_pGameRules->IsSpawnPointValid( pSpot, NULL ) ) + { + // the successful spawn point's location + NDebugOverlay::Box( pSpot->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 0, 255, 0, 100, 60 ); + + // drop down to ground + Vector GroundPos = DropToGround( NULL, pSpot->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX ); + + // the location the player will spawn at + NDebugOverlay::Box( GroundPos, VEC_HULL_MIN, VEC_HULL_MAX, 0, 0, 255, 100, 60 ); + + // draw the spawn angles + QAngle spotAngles = pSpot->GetLocalAngles(); + Vector vecForward; + AngleVectors( spotAngles, &vecForward ); + NDebugOverlay::HorzArrow( pSpot->GetAbsOrigin(), pSpot->GetAbsOrigin() + vecForward * 32, 10, 255, 0, 0, 255, true, 60 ); + } + else + { + // failed spawn point location + NDebugOverlay::Box( pSpot->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 0, 100, 60 ); + } + + // increment pSpot + pSpot = gEntList.FindEntityByClassname( pSpot, pEntClassName ); + } + } + + void TestSpawns() + { + TestSpawnPointType( "info_player_allies" ); + TestSpawnPointType( "info_player_axis" ); + } + ConCommand cc_TestSpawns( "map_showspawnpoints", TestSpawns, "Dev - test the spawn points, draws for 60 seconds", FCVAR_CHEAT ); + + CBaseEntity *CDODGameRules::GetPlayerSpawnSpot( CBasePlayer *pPlayer ) + { + // get valid spawn point + CBaseEntity *pSpawnSpot = pPlayer->EntSelectSpawnPoint(); + + // drop down to ground + Vector GroundPos = DropToGround( pPlayer, pSpawnSpot->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX ); + + // Move the player to the place it said. + pPlayer->Teleport( &GroundPos, &pSpawnSpot->GetLocalAngles(), &vec3_origin ); + pPlayer->m_Local.m_vecPunchAngle = vec3_angle; + + return pSpawnSpot; + } + + // checks if the spot is clear of players + bool CDODGameRules::IsSpawnPointValid( CBaseEntity *pSpot, CBasePlayer *pPlayer ) + { + if ( !pSpot->IsTriggered( pPlayer ) ) + { + return false; + } + + // Check if it is disabled by Enable/Disable + CSpawnPoint *pSpawnPoint = dynamic_cast< CSpawnPoint * >( pSpot ); + if ( pSpawnPoint ) + { + if ( pSpawnPoint->IsDisabled() ) + { + return false; + } + } + + Vector mins = GetViewVectors()->m_vHullMin; + Vector maxs = GetViewVectors()->m_vHullMax; + + Vector vTestMins = pSpot->GetAbsOrigin() + mins; + Vector vTestMaxs = pSpot->GetAbsOrigin() + maxs; + + // First test the starting origin. + return UTIL_IsSpaceEmpty( pPlayer, vTestMins, vTestMaxs ); + } + + void CDODGameRules::PlayerSpawn( CBasePlayer *p ) + { + CDODPlayer *pPlayer = ToDODPlayer( p ); + + int team = pPlayer->GetTeamNumber(); + + if( team == TEAM_ALLIES || team == TEAM_AXIS ) + { + int iPreviousPlayerClass = pPlayer->m_Shared.PlayerClass(); + + if( pPlayer->m_Shared.DesiredPlayerClass() == PLAYERCLASS_RANDOM ) + { + ChooseRandomClass( pPlayer ); + ClientPrint( pPlayer, HUD_PRINTTALK, "#game_now_as", GetPlayerClassName( pPlayer->m_Shared.PlayerClass(), team ) ); + } + else + { + pPlayer->m_Shared.SetPlayerClass( pPlayer->m_Shared.DesiredPlayerClass() ); + } + + int playerclass = pPlayer->m_Shared.PlayerClass(); + + if ( playerclass != iPreviousPlayerClass ) + { + // spawning as a new class, flush stats + pPlayer->StatEvent_UploadStats(); + } + + if( playerclass != PLAYERCLASS_UNDEFINED ) + { + //Assert( PLAYERCLASS_UNDEFINED < playerclass && playerclass < NUM_PLAYERCLASSES ); + + int i; + + CDODTeam *pTeam = GetGlobalDODTeam( team ); + const CDODPlayerClassInfo &pClassInfo = pTeam->GetPlayerClassInfo( playerclass ); + + Assert( pClassInfo.m_iTeam == team ); + + pPlayer->SetModel( pClassInfo.m_szPlayerModel ); + pPlayer->SetHitboxSet( 0 ); + + char buf[64]; + int bufsize = sizeof(buf); + + //Give weapons + + // Primary weapon + Q_snprintf( buf, bufsize, "weapon_%s", WeaponIDToAlias(pClassInfo.m_iPrimaryWeapon) ); + CBaseEntity *pPrimaryWpn = pPlayer->GiveNamedItem( buf ); + Assert( pPrimaryWpn ); + + // Secondary weapon + CBaseEntity *pSecondaryWpn = NULL; + if ( pClassInfo.m_iSecondaryWeapon != WEAPON_NONE ) + { + Q_snprintf( buf, bufsize, "weapon_%s", WeaponIDToAlias(pClassInfo.m_iSecondaryWeapon) ); + pSecondaryWpn = pPlayer->GiveNamedItem( buf ); + } + + // Melee weapon + if ( pClassInfo.m_iMeleeWeapon ) + { + Q_snprintf( buf, bufsize, "weapon_%s", WeaponIDToAlias(pClassInfo.m_iMeleeWeapon) ); + pPlayer->GiveNamedItem( buf ); + } + + CWeaponDODBase *pWpn = NULL; + + // Primary Ammo + pWpn = dynamic_cast<CWeaponDODBase *>(pPrimaryWpn); + + if( pWpn ) + { + int iNumClip = pWpn->GetDODWpnData().m_iDefaultAmmoClips - 1; //account for one clip in the gun + int iClipSize = pWpn->GetDODWpnData().iMaxClip1; + pPlayer->GiveAmmo( iNumClip * iClipSize, pWpn->GetDODWpnData().szAmmo1 ); + } + + // Secondary Ammo + if ( pSecondaryWpn ) + { + pWpn = dynamic_cast<CWeaponDODBase *>(pSecondaryWpn); + + if( pWpn ) + { + int iNumClip = pWpn->GetDODWpnData().m_iDefaultAmmoClips - 1; //account for one clip in the gun + int iClipSize = pWpn->GetDODWpnData().iMaxClip1; + pPlayer->GiveAmmo( iNumClip * iClipSize, pWpn->GetDODWpnData().szAmmo1 ); + } + } + + // Grenade Type 1 + if ( pClassInfo.m_iGrenType1 != WEAPON_NONE ) + { + Q_snprintf( buf, bufsize, "weapon_%s", WeaponIDToAlias(pClassInfo.m_iGrenType1) ); + for ( i=0;i<pClassInfo.m_iNumGrensType1;i++ ) + { + pPlayer->GiveNamedItem( buf ); + } + } + + // Grenade Type 2 + if ( pClassInfo.m_iGrenType2 != WEAPON_NONE ) + { + Q_snprintf( buf, bufsize, "weapon_%s", WeaponIDToAlias(pClassInfo.m_iGrenType2) ); + for ( i=0;i<pClassInfo.m_iNumGrensType2;i++ ) + { + pPlayer->GiveNamedItem( buf ); + } + } + + pPlayer->Weapon_Switch( (CBaseCombatWeapon *)pPrimaryWpn ); + + // you get a helmet + pPlayer->SetBodygroup( BODYGROUP_HELMET, pClassInfo.m_iHelmetGroup ); + + // no jumpgear + pPlayer->SetBodygroup( BODYGROUP_JUMPGEAR, BODYGROUP_JUMPGEAR_OFF ); + + pPlayer->SetMaxSpeed( 600 ); + + Assert( playerclass >= 0 && playerclass <= 5 ); + if ( playerclass >= 0 && playerclass <= 5 ) + { + if ( team == TEAM_ALLIES ) + m_iStatsSpawnsPerClass_Allies[playerclass]++; + else if ( team == TEAM_AXIS ) + m_iStatsSpawnsPerClass_Axis[playerclass]++; + } + } + else + { + Assert( !"Player spawning with PLAYERCLASS_UNDEFINED" ); + pPlayer->SetModel( NULL ); + } + } + } + + const char *CDODGameRules::GetPlayerClassName( int cls, int team ) + { + CDODTeam *pTeam = GetGlobalDODTeam( team ); + + if( cls == PLAYERCLASS_RANDOM ) + { + return "#class_random"; + } + + if( cls < 0 || cls >= pTeam->GetNumPlayerClasses() ) + { + Assert( false ); + return NULL; + } + + const CDODPlayerClassInfo &pClassInfo = pTeam->GetPlayerClassInfo( cls ); + + return pClassInfo.m_szPrintName; + } + + void CDODGameRules::ChooseRandomClass( CDODPlayer *pPlayer ) + { + int i; + int numChoices = 0; + int choices[16]; + int firstclass = 0; + + CDODTeam *pTeam = GetGlobalDODTeam( pPlayer->GetTeamNumber() ); + + int lastclass = pTeam->GetNumPlayerClasses(); + + int previousClass = pPlayer->m_Shared.PlayerClass(); + + // Compile a list of the classes that aren't full + for( i=firstclass;i<lastclass;i++ ) + { + // don't join the same class twice in a row + if ( i == previousClass ) + continue; + + if( CanPlayerJoinClass( pPlayer, i ) ) + { + choices[numChoices] = i; + numChoices++; + } + } + + // If ALL the classes are full + if( numChoices == 0 ) + { + Msg( "Random class found that all classes were full - ignoring class limits for this spawn\n" ); + + pPlayer->m_Shared.SetPlayerClass( random->RandomFloat( firstclass, lastclass ) ); + } + else + { + // Choose a slot randomly + i = random->RandomInt( 0, numChoices-1 ); + + // We are now the class that was in that slot + pPlayer->m_Shared.SetPlayerClass( choices[i] ); + } + } + + //----------------------------------------------------------------------------- + // Purpose: This function can be used to find a valid placement location for an entity. + // Given an origin to start looking from and a minimum radius to place the entity at, + // it will sweep out a circle around vOrigin and try to find a valid spot (on the ground) + // where mins and maxs will fit. + // Input : *pMainEnt - Entity to place + // &vOrigin - Point to search around + // fRadius - Radius to search within + // nTries - Number of tries to attempt + // &mins - mins of the Entity + // &maxs - maxs of the Entity + // &outPos - Return point + // Output : Returns true and fills in outPos if it found a spot. + //----------------------------------------------------------------------------- + bool EntityPlacementTest( CBaseEntity *pMainEnt, const Vector &vOrigin, Vector &outPos, bool bDropToGround ) + { + // This function moves the box out in each dimension in each step trying to find empty space like this: + // + // X + // X X + // Step 1: X Step 2: XXX Step 3: XXXXX + // X X + // X + // + + Vector mins, maxs; + pMainEnt->CollisionProp()->WorldSpaceAABB( &mins, &maxs ); + mins -= pMainEnt->GetAbsOrigin(); + maxs -= pMainEnt->GetAbsOrigin(); + + // Put some padding on their bbox. + + Vector vTestMins = mins; + Vector vTestMaxs = maxs; + + // First test the starting origin. + if ( UTIL_IsSpaceEmpty( pMainEnt, vOrigin + vTestMins, vOrigin + vTestMaxs ) ) + { + if ( bDropToGround ) + { + outPos = DropToGround( pMainEnt, vOrigin, vTestMins, vTestMaxs ); + } + else + { + outPos = vOrigin; + } + return true; + } + + Vector vDims = vTestMaxs - vTestMins; + + // Keep branching out until we get too far. + int iCurIteration = 0; + int nMaxIterations = 15; + + int offset = 0; + do + { + for ( int iDim=0; iDim < 2; iDim++ ) + { + float flCurOffset = offset * vDims[iDim]; + + for ( int iSign=0; iSign < 2; iSign++ ) + { + Vector vBase = vOrigin; + vBase[iDim] += (iSign*2-1) * flCurOffset; + + if ( UTIL_IsSpaceEmpty( pMainEnt, vBase + vTestMins, vBase + vTestMaxs ) ) + { + // Ensure that there is a clear line of sight from the spawnpoint entity to the actual spawn point. + // (Useful for keeping things from spawning behind walls near a spawn point) + trace_t tr; + UTIL_TraceLine( vOrigin, vBase, MASK_SOLID, pMainEnt, COLLISION_GROUP_NONE, &tr ); + + if ( tr.fraction != 1.0 ) + { + continue; + } + + if ( bDropToGround ) + outPos = DropToGround( pMainEnt, vBase, vTestMins, vTestMaxs ); + else + outPos = vBase; + + return true; + } + } + } + + ++offset; + } while ( iCurIteration++ < nMaxIterations ); + + // Warning( "EntityPlacementTest for ent %d:%s failed!\n", pMainEnt->entindex(), pMainEnt->GetClassname() ); + return false; + } + + bool CDODGameRules::CanHavePlayerItem( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon ) + { + //only allow one primary, one secondary and one melee + CWeaponDODBase *pWpn = (CWeaponDODBase *)pWeapon; + + if( pWpn ) + { + int type = pWpn->GetDODWpnData().m_WeaponType; + + switch( type ) + { + case WPN_TYPE_MELEE: + { +#ifdef DEBUG + CWeaponDODBase *pMeleeWeapon = (CWeaponDODBase *)pPlayer->Weapon_GetSlot( WPN_SLOT_MELEE ); + bool bHasMelee = ( pMeleeWeapon != NULL ); + + if( bHasMelee ) + { + Assert( !"Why are we trying to add another melee?" ); + return false; + } +#endif + } + break; + case WPN_TYPE_PISTOL: + case WPN_TYPE_SIDEARM: + { +#ifdef DEBUG + CWeaponDODBase *pSecondaryWeapon = (CWeaponDODBase *)pPlayer->Weapon_GetSlot( WPN_SLOT_SECONDARY ); + bool bHasPistol = ( pSecondaryWeapon != NULL ); + + if( bHasPistol ) + { + Assert( !"Why are we trying to add another pistol?" ); + return false; + } +#endif + } + break; + + case WPN_TYPE_CAMERA: + return true; + + case WPN_TYPE_RIFLE: + case WPN_TYPE_SNIPER: + case WPN_TYPE_SUBMG: + case WPN_TYPE_MG: + case WPN_TYPE_BAZOOKA: + { + //Don't pick up dropped weapons if we have one already + CWeaponDODBase *pPrimaryWeapon = (CWeaponDODBase *)pPlayer->Weapon_GetSlot( WPN_SLOT_PRIMARY ); + bool bHasPrimary = ( pPrimaryWeapon != NULL ); + + if( bHasPrimary ) + return false; + } + break; + + default: + break; + } + } + + return BaseClass::CanHavePlayerItem( pPlayer, pWeapon ); + } + + void CDODGameRules::ResetMapTime( void ) + { + m_flMapResetTime = gpGlobals->curtime; + + // send an event with the time remaining until map change + + IGameEvent *event = gameeventmanager->CreateEvent( "dod_map_time_remaining" ); + if ( event ) + { + event->SetInt( "seconds", GetTimeLeft() ); + gameeventmanager->FireEvent( event ); + } + } + +#endif + +bool CDODGameRules::ShouldCollide( int collisionGroup0, int collisionGroup1 ) +{ + if ( collisionGroup0 > collisionGroup1 ) + { + // swap so that lowest is always first + V_swap(collisionGroup0,collisionGroup1); + } + + //Don't stand on COLLISION_GROUP_WEAPONs + if( collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT && + collisionGroup1 == COLLISION_GROUP_WEAPON ) + { + return false; + } + + // TE shells don't collide with the player + if ( collisionGroup0 == COLLISION_GROUP_PLAYER && + collisionGroup1 == DOD_COLLISIONGROUP_SHELLS ) + { + return false; + } + + // blocker walls only collide with players + if ( collisionGroup1 == DOD_COLLISIONGROUP_BLOCKERWALL ) + return ( collisionGroup0 == COLLISION_GROUP_PLAYER ) || ( collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT ); + + return BaseClass::ShouldCollide( collisionGroup0, collisionGroup1 ); +} + +int CDODGameRules::GetSubTeam( int team ) +{ + return SUBTEAM_NORMAL; +} + +bool CDODGameRules::IsGameUnderTimeLimit( void ) +{ + return ( mp_timelimit.GetInt() > 0 ); +} + +int CDODGameRules::GetTimeLeft( void ) +{ + float flTimeLimit = mp_timelimit.GetInt() * 60; + + Assert( flTimeLimit > 0 && "Should not call this function when !IsGameUnderTimeLimit" ); + + float flMapChangeTime = m_flMapResetTime + flTimeLimit; + +#ifndef CLIENT_DLL + // If the round timer is longer, let the round complete + if ( m_bUsingTimer && m_pRoundTimer ) + { + float flTimerSeconds = m_pRoundTimer->GetTimeRemaining(); + float flMapChangeSeconds = flMapChangeTime - gpGlobals->curtime; + + // if the map timer is less than the round timer + // AND + // the round timer is less than 2 minutes + + + // If the map time for any reason goes beyond the end of the round, remove the flag + if ( flMapChangeSeconds > flTimerSeconds ) + { + m_bChangeLevelOnRoundEnd = false; + } + else if ( m_bChangeLevelOnRoundEnd || flTimerSeconds < 120 ) + { + // once this happens once in a round, use this until the round ends + // or else the round will end when a team captures an objective and adds time to above 120 + m_bChangeLevelOnRoundEnd = true; + + return (int)( flTimerSeconds ); + } + } +#endif + + return ( (int)(flMapChangeTime - gpGlobals->curtime) ); +} + +int CDODGameRules::GetReinforcementTimerSeconds( int team, float flSpawnEligibleTime ) +{ + // find the first wave that this player can fit in + + float flWaveTime = -1; + + switch( team ) + { + case TEAM_ALLIES: + { + int i = m_iAlliesRespawnHead; + + while( i != m_iAlliesRespawnTail ) + { + if ( flSpawnEligibleTime < m_AlliesRespawnQueue[i] ) + { + flWaveTime = m_AlliesRespawnQueue[i]; + break; + } + + i = ( i+1 ) % DOD_RESPAWN_QUEUE_SIZE; + } + } + break; + case TEAM_AXIS: + { + int i = m_iAxisRespawnHead; + + while( i != m_iAxisRespawnTail ) + { + if ( flSpawnEligibleTime < m_AxisRespawnQueue[i] ) + { + flWaveTime = m_AxisRespawnQueue[i]; + break; + } + + i = ( i+1 ) % DOD_RESPAWN_QUEUE_SIZE; + } + } + break; + default: + return -1; + } + + return MAX( 0, (int)( flWaveTime - gpGlobals->curtime ) ); +} + +const CViewVectors* CDODGameRules::GetViewVectors() const +{ + return &g_DODViewVectors; +} + +const CDODViewVectors *CDODGameRules::GetDODViewVectors() const +{ + return &g_DODViewVectors; +} + +#ifndef CLIENT_DLL + + extern ConVar dod_bonusround; + + bool CDODGameRules::IsFriendlyFireOn( void ) + { + // Never friendly fire in bonus round + if ( IsInBonusRound() ) + { + return false; + } + + return friendlyfire.GetBool(); + } + + bool CDODGameRules::IsInBonusRound( void ) + { + return ( dod_bonusround.GetBool() == true && ( State_Get() == STATE_ALLIES_WIN || State_Get() == STATE_AXIS_WIN ) ); + } + + ConVar dod_showroundtransitions( "dod_showroundtransitions", "0", 0, "Show gamestate round transitions" ); + + void CDODGameRules::State_Transition( DODRoundState newState ) + { + State_Leave(); + State_Enter( newState ); + } + + void CDODGameRules::State_Enter( DODRoundState newState ) + { + m_iRoundState = newState; + m_pCurStateInfo = State_LookupInfo( newState ); + + if ( dod_showroundtransitions.GetInt() > 0 ) + { + if ( m_pCurStateInfo ) + Msg( "DODRoundState: entering '%s'\n", m_pCurStateInfo->m_pStateName ); + else + Msg( "DODRoundState: entering #%d\n", newState ); + } + + // Initialize the new state. + if ( m_pCurStateInfo && m_pCurStateInfo->pfnEnterState ) + (this->*m_pCurStateInfo->pfnEnterState)(); + } + + void CDODGameRules::State_Leave() + { + if ( m_pCurStateInfo && m_pCurStateInfo->pfnLeaveState ) + { + (this->*m_pCurStateInfo->pfnLeaveState)(); + } + } + + + void CDODGameRules::State_Think() + { + if ( m_pCurStateInfo && m_pCurStateInfo->pfnThink ) + { + (this->*m_pCurStateInfo->pfnThink)(); + } + } + + + CDODRoundStateInfo* CDODGameRules::State_LookupInfo( DODRoundState state ) + { + static CDODRoundStateInfo playerStateInfos[] = + { + { STATE_INIT, "STATE_INIT", &CDODGameRules::State_Enter_INIT, NULL, &CDODGameRules::State_Think_INIT }, + { STATE_PREGAME, "STATE_PREGAME", &CDODGameRules::State_Enter_PREGAME, NULL, &CDODGameRules::State_Think_PREGAME }, + { STATE_STARTGAME, "STATE_STARTGAME", &CDODGameRules::State_Enter_STARTGAME, NULL, &CDODGameRules::State_Think_STARTGAME }, + { STATE_PREROUND, "STATE_PREROUND", &CDODGameRules::State_Enter_PREROUND, NULL, &CDODGameRules::State_Think_PREROUND }, + { STATE_RND_RUNNING,"STATE_RND_RUNNING",&CDODGameRules::State_Enter_RND_RUNNING, NULL, &CDODGameRules::State_Think_RND_RUNNING }, + { STATE_ALLIES_WIN, "STATE_ALLIES_WIN", &CDODGameRules::State_Enter_ALLIES_WIN, NULL, &CDODGameRules::State_Think_ALLIES_WIN }, + { STATE_AXIS_WIN, "STATE_AXIS_WIN", &CDODGameRules::State_Enter_AXIS_WIN, NULL, &CDODGameRules::State_Think_AXIS_WIN }, + { STATE_RESTART, "STATE_RESTART", &CDODGameRules::State_Enter_RESTART, NULL, &CDODGameRules::State_Think_RESTART }, + { STATE_GAME_OVER, "STATE_GAME_OVER", NULL, NULL, NULL }, + }; + + for ( int i=0; i < ARRAYSIZE( playerStateInfos ); i++ ) + { + if ( playerStateInfos[i].m_iRoundState == state ) + return &playerStateInfos[i]; + } + + return NULL; + } + + extern ConVar sv_stopspeed; + extern ConVar sv_friction; + + void CDODGameRules::State_Enter_INIT( void ) + { + InitTeams(); + + sv_stopspeed.SetValue( 50.0f ); + sv_friction.SetValue( 8.0f ); + + ResetMapTime(); + } + + void CDODGameRules::State_Think_INIT( void ) + { + State_Transition( STATE_PREGAME ); + } + + void CDODGameRules::InitTeams( void ) + { + Assert( g_Teams.Count() == 0 ); + + g_Teams.Purge(); // just in case + + // Create the team managers + int i; + for ( i = 0; i < 2; i++ ) // Unassigned and Spectators + { + CTeam *pTeam = static_cast<CTeam*>(CreateEntityByName( "dod_team_manager" )); + pTeam->Init( sTeamNames[i], i ); + + g_Teams.AddToTail( pTeam ); + } + + // clear the player class data + ResetFilePlayerClassInfoDatabase(); + + CTeam *pAllies = static_cast<CTeam*>(CreateEntityByName( "dod_team_allies" )); + Assert( pAllies ); + pAllies->Init( sTeamNames[TEAM_ALLIES], TEAM_ALLIES ); + g_Teams.AddToTail( pAllies ); + + CTeam *pAxis = static_cast<CTeam*>(CreateEntityByName( "dod_team_axis" )); + Assert( pAxis ); + pAxis->Init( sTeamNames[TEAM_AXIS], TEAM_AXIS ); + g_Teams.AddToTail( pAxis ); + } + + // dod_control_point_master can take inputs to add time to the round timer + void CDODGameRules::AddTimerSeconds( int iSecondsToAdd ) + { + if( m_bUsingTimer && m_pRoundTimer ) + { + m_pRoundTimer->AddTimerSeconds( iSecondsToAdd ); + + float flTimerSeconds = m_pRoundTimer->GetTimeRemaining(); + + m_bPlayTimerWarning_1Minute = ( flTimerSeconds > 60 ); + m_bPlayTimerWarning_2Minute = ( flTimerSeconds > 120 ); + + IGameEvent *event = gameeventmanager->CreateEvent( "dod_timer_time_added" ); + if ( event ) + { + event->SetInt( "seconds_added", iSecondsToAdd ); + gameeventmanager->FireEvent( event ); + } + } + } + + int CDODGameRules::GetTimerSeconds( void ) + { + if( m_bUsingTimer && m_pRoundTimer ) + { + return m_pRoundTimer->GetTimeRemaining(); + } + else + { + return 0; + } + } + + // PREGAME - the server is idle and waiting for enough + // players to start up again. When we find an active player + // go to STATE_STARTGAME + void CDODGameRules::State_Enter_PREGAME( void ) + { + m_flNextPeriodicThink = gpGlobals->curtime + 0.1; + + Load_EntText(); + } + + void CDODGameRules::State_Think_PREGAME( void ) + { + CheckLevelInitialized(); + + if( CountActivePlayers() > 0 ) + State_Transition( STATE_STARTGAME ); + } + + // STARTGAME - wait a bit and then spawn everyone into the + // preround + void CDODGameRules::State_Enter_STARTGAME( void ) + { + m_flStateTransitionTime = gpGlobals->curtime + 5 * dod_enableroundwaittime.GetFloat(); + + m_bInitialSpawn = true; + } + + void CDODGameRules::State_Think_STARTGAME() + { + if( gpGlobals->curtime > m_flStateTransitionTime ) + { + if( mp_warmup_time.GetFloat() > 0 ) + { + // go into warmup, reset at end of it + SetInWarmup( true ); + } + + State_Transition( STATE_PREROUND ); + } + } + + void CDODGameRules::State_Enter_PREROUND( void ) + { + // Longer wait time if its the first round, let people join + if ( m_bInitialSpawn ) + { + m_flStateTransitionTime = gpGlobals->curtime + 10 * dod_enableroundwaittime.GetFloat(); + m_bInitialSpawn = false; + } + else + { + m_flStateTransitionTime = gpGlobals->curtime + 5 * dod_enableroundwaittime.GetFloat(); + } + + //Game rules may change, if a new one becomes mastered at the end of the last round + DetectGameRules(); + + //reset everything in the level + RoundRespawn(); + + // reset this now! If its reset at round restart, we lose all the players that died + // during the preround + m_iAlliesRespawnHead = 0; + m_iAlliesRespawnTail = 0; + m_iAxisRespawnHead = 0; + m_iAxisRespawnTail = 0; + m_iNumAlliesRespawnWaves = 0; + m_iNumAxisRespawnWaves = 0; + + m_iLastAlliesCapEvent = CAP_EVENT_NONE; + m_iLastAxisCapEvent = CAP_EVENT_NONE; + + //find all the control points, init the timer + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "dod_control_point_master" ); + + if( !pEnt ) + { + Warning( "No dod_control_point_master found in level - control points will not work as expected.\n" ); + } + + bool bFoundTimer = false; + + while( pEnt ) + { + variant_t emptyVariant; + pEnt->AcceptInput( "RoundInit", NULL, NULL, emptyVariant, 0 ); + + CControlPointMaster *pMaster = dynamic_cast<CControlPointMaster *>( pEnt ); + + if ( pMaster && pMaster->IsActive() ) + { + if ( pMaster->IsUsingRoundTimer() ) + { + bFoundTimer = true; + + m_bUsingTimer = true; + + int iTimerSeconds; + + pMaster->GetTimerData( iTimerSeconds, m_iTimerWinTeam ); + + if ( m_iTimerWinTeam != TEAM_ALLIES && m_iTimerWinTeam != TEAM_AXIS ) + { + Assert( !"Round timer win team can only be allies or axis!\n" ); + } + + // Timer starts paused + if ( !m_pRoundTimer.Get() ) + { + m_pRoundTimer = ( CDODRoundTimer *) CreateEntityByName( "dod_round_timer" ); + } + + Assert( m_pRoundTimer ); + + if ( m_pRoundTimer ) + { + m_pRoundTimer->SetTimeRemaining( iTimerSeconds ); + m_pRoundTimer->PauseTimer(); + + m_bPlayTimerWarning_1Minute = ( iTimerSeconds > 60 ); + m_bPlayTimerWarning_2Minute = ( iTimerSeconds > 120 ); + } + } + } + + pEnt = gEntList.FindEntityByClassname( pEnt, "dod_control_point_master" ); + } + + if ( bFoundTimer == false ) + { + // No masters are active that require the round timer, destroy it + UTIL_Remove( m_pRoundTimer.Get() ); + m_pRoundTimer = NULL; + } + + //init the cap areas + pEnt = gEntList.FindEntityByClassname( NULL, "dod_capture_area" ); + while( pEnt ) + { + variant_t emptyVariant; + pEnt->AcceptInput( "RoundInit", NULL, NULL, emptyVariant, 0 ); + + pEnt = gEntList.FindEntityByClassname( pEnt, "dod_capture_area" ); + } + + IGameEvent *event = gameeventmanager->CreateEvent( "dod_round_start" ); + if ( event ) + gameeventmanager->FireEvent( event ); + + // figure out which teams are bombing + m_bAlliesAreBombing = false; + m_bAxisAreBombing = false; + + pEnt = gEntList.FindEntityByClassname( NULL, "dod_bomb_target" ); + while( pEnt ) + { + CDODBombTarget *pTarget = dynamic_cast<CDODBombTarget *>( pEnt ); + + if ( pTarget && pTarget->State_Get() == BOMB_TARGET_ACTIVE ) + { + switch( pTarget->GetBombingTeam() ) + { + case TEAM_ALLIES: + m_bAlliesAreBombing = true; + break; + case TEAM_AXIS: + m_bAxisAreBombing = true; + break; + default: + break; + } + } + + pEnt = gEntList.FindEntityByClassname( pEnt, "dod_bomb_target" ); + } + } + + void CDODGameRules::State_Think_PREROUND( void ) + { + if( gpGlobals->curtime > m_flStateTransitionTime ) + State_Transition( STATE_RND_RUNNING ); + + CheckRespawnWaves(); + } + + void CDODGameRules::State_Enter_RND_RUNNING( void ) + { + //find all the control points, init the timer + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "dod_control_point_master" ); + + while( pEnt ) + { + variant_t emptyVariant; + pEnt->AcceptInput( "RoundStart", NULL, NULL, emptyVariant, 0 ); + + pEnt = gEntList.FindEntityByClassname( pEnt, "dod_control_point_master" ); + } + + IGameEvent *event = gameeventmanager->CreateEvent( "dod_round_active" ); + if ( event ) + gameeventmanager->FireEvent( event ); + + if( !IsInWarmup() ) + PlayStartRoundVoice(); + + if ( m_bUsingTimer && m_pRoundTimer.Get() != NULL ) + { + m_pRoundTimer->ResumeTimer(); + } + + m_bChangeLevelOnRoundEnd = false; + } + + void CDODGameRules::State_Think_RND_RUNNING( void ) + { + //Where the magic happens + + if ( m_bUsingTimer && m_pRoundTimer ) + { + float flSecondsRemaining = m_pRoundTimer->GetTimeRemaining(); + + if ( flSecondsRemaining <= 0 ) + { + // if there is a bomb still on a timer, and that bomb has + // the potential to add time, then we don't end the game + + bool bBombBlocksWin = false; + + //find all the control points, init the timer + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "dod_bomb_target" ); + + while( pEnt ) + { + CDODBombTarget *pBomb = dynamic_cast<CDODBombTarget *>( pEnt ); + + // Find active bombs that have the potential to add round time + if ( pBomb && pBomb->State_Get() == BOMB_TARGET_ARMED ) + { + if ( pBomb->GetTimerAddSeconds() > 0 ) + { + // don't end the round until this bomb goes off or is disarmed + bBombBlocksWin = true; + break; + } + + CControlPoint *pPoint = pBomb->GetControlPoint(); + int iBombingTeam = pBomb->GetBombingTeam(); + + if ( pPoint && pPoint->GetBombsRemaining() <= 1 ) + { + // find active dod_control_point_masters, ask them if this flag capping + // would end the game + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "dod_control_point_master" ); + + while( pEnt ) + { + CControlPointMaster *pMaster = dynamic_cast<CControlPointMaster *>( pEnt ); + + if ( pMaster->IsActive() ) + { + // Check TeamOwnsAllPoints, while overriding this particular flag's owner + if ( pMaster->WouldNewCPOwnerWinGame( pPoint, iBombingTeam ) ) + { + // This bomb may win the game, don't end the round. + bBombBlocksWin = true; + break; + } + } + + pEnt = gEntList.FindEntityByClassname( pEnt, "dod_control_point_master" ); + } + } + } + + pEnt = gEntList.FindEntityByClassname( pEnt, "dod_bomb_target" ); + } + + if ( bBombBlocksWin == false ) + { + SetWinningTeam( m_iTimerWinTeam ); + + // tell the dod_control_point_master to fire its outputs for the winning team! + // minor hackage - dod_gamerules should be responsible for team win events, not dod_cpm + + //find all the control points, init the timer + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "dod_control_point_master" ); + + while( pEnt ) + { + CControlPointMaster *pMaster = dynamic_cast<CControlPointMaster *>( pEnt ); + + if ( pMaster->IsActive() ) + { + pMaster->FireTeamWinOutput( m_iTimerWinTeam ); + } + + pEnt = gEntList.FindEntityByClassname( pEnt, "dod_control_point_master" ); + } + } + } + else if ( flSecondsRemaining < 60.0 && m_bPlayTimerWarning_1Minute == true ) + { + // play one minute warning + DevMsg( 1, "Timer Warning: 1 Minute Remaining\n" ); + + IGameEvent *event = gameeventmanager->CreateEvent( "dod_timer_flash" ); + if ( event ) + { + event->SetInt( "time_remaining", 60 ); + gameeventmanager->FireEvent( event ); + } + + m_bPlayTimerWarning_1Minute = false; + } + else if ( flSecondsRemaining < 120.0 && m_bPlayTimerWarning_2Minute == true ) + { + // play two minute warning + DevMsg( 1, "Timer Warning: 2 Minutes Remaining\n" ); + + IGameEvent *event = gameeventmanager->CreateEvent( "dod_timer_flash" ); + if ( event ) + { + event->SetInt( "time_remaining", 120 ); + gameeventmanager->FireEvent( event ); + } + + m_bPlayTimerWarning_2Minute = false; + } + } + + //if we don't find any active players, return to STATE_PREGAME + if( CountActivePlayers() <= 0 ) + { + State_Transition( STATE_PREGAME ); + return; + } + + CheckRespawnWaves(); + + // check round restart + if( m_flRestartRoundTime > 0 && m_flRestartRoundTime < gpGlobals->curtime ) + { + // time to restart! + State_Transition( STATE_RESTART ); + m_flRestartRoundTime = -1; + } + + // check ready restart + if( m_bAwaitingReadyRestart && m_bHeardAlliesReady && m_bHeardAxisReady ) + { + //State_Transition( STATE_RESTART ); + m_flRestartRoundTime = gpGlobals->curtime + 5; + m_bAwaitingReadyRestart = false; + } + } + + void CDODGameRules::CheckRespawnWaves( void ) + { + bool bDoFailSafeWaveCheck = false; + + if ( m_flNextFailSafeWaveCheckTime < gpGlobals->curtime ) + { + bDoFailSafeWaveCheck = true; + m_flNextFailSafeWaveCheckTime = gpGlobals->curtime + 3.0; + } + + //Respawn Timers + if( m_iNumAlliesRespawnWaves > 0 ) + { + if ( m_AlliesRespawnQueue[m_iAlliesRespawnHead] < gpGlobals->curtime ) + { + DevMsg( "Wave: Respawning Allies\n" ); + + RespawnTeam( TEAM_ALLIES ); + + PopWaveTime( TEAM_ALLIES ); + } + } + else if ( bDoFailSafeWaveCheck ) + { + // if there are any allied people waiting to spawn, spawn them + FailSafeSpawnPlayersOnTeam( TEAM_ALLIES ); + } + + if( m_iNumAxisRespawnWaves > 0 ) + { + if ( m_AxisRespawnQueue[m_iAxisRespawnHead] < gpGlobals->curtime ) + { + DevMsg( "Wave: Respawning Axis\n" ); + + RespawnTeam( TEAM_AXIS ); + + PopWaveTime( TEAM_AXIS ); + } + } + else if ( bDoFailSafeWaveCheck ) + { + // if there are any axis people waiting to spawn, spawn them + FailSafeSpawnPlayersOnTeam( TEAM_AXIS ); + } + } + + void CDODGameRules::FailSafeSpawnPlayersOnTeam( int iTeam ) + { + DODRoundState roundState = State_Get(); + + CDODTeam *pTeam = GetGlobalDODTeam( iTeam ); + if ( pTeam ) + { + int iNumPlayers = pTeam->GetNumPlayers(); + for ( int i=0;i<iNumPlayers;i++ ) + { + CDODPlayer *pPlayer = pTeam->GetDODPlayer(i); + if ( !pPlayer ) + continue; + + // if this player is waiting to spawn, spawn them + + if ( pPlayer->IsAlive() ) + continue; + + if( pPlayer->m_Shared.DesiredPlayerClass() == PLAYERCLASS_UNDEFINED ) + continue; + + if ( gpGlobals->curtime < ( pPlayer->GetDeathTime() + DEATH_CAM_TIME ) ) + continue; + + if ( pPlayer->GetObserverMode() == OBS_MODE_FREEZECAM ) + continue; + + if ( roundState != STATE_PREROUND && pPlayer->State_Get() == STATE_DEATH_ANIM ) + continue; + + // Respawn this player + pPlayer->DODRespawn(); + + Assert( !"This will happen, but see if we can figure out why we get here" ); + } + } + } + + //ALLIES WIN + void CDODGameRules::State_Enter_ALLIES_WIN( void ) + { + float flTime = MAX( 5, dod_bonusroundtime.GetFloat() ); + + m_flStateTransitionTime = gpGlobals->curtime + flTime * dod_enableroundwaittime.GetFloat(); + + if ( m_bUsingTimer && m_pRoundTimer ) + { + m_pRoundTimer->PauseTimer(); + } + } + + void CDODGameRules::State_Think_ALLIES_WIN( void ) + { + if( gpGlobals->curtime > m_flStateTransitionTime ) + { + State_Transition( STATE_PREROUND ); + } + } + + //AXIS WIN + void CDODGameRules::State_Enter_AXIS_WIN( void ) + { + float flTime = MAX( 5, dod_bonusroundtime.GetFloat() ); + + m_flStateTransitionTime = gpGlobals->curtime + flTime * dod_enableroundwaittime.GetFloat(); + + if ( m_bUsingTimer && m_pRoundTimer ) + { + m_pRoundTimer->PauseTimer(); + } + } + + void CDODGameRules::State_Think_AXIS_WIN( void ) + { + if( gpGlobals->curtime > m_flStateTransitionTime ) + { + State_Transition( STATE_PREROUND ); + } + } + + // manual restart + void CDODGameRules::State_Enter_RESTART( void ) + { + // send scores + SendTeamScoresEvent(); + + // send restart event + IGameEvent *event = gameeventmanager->CreateEvent( "dod_restart_round" ); + if ( event ) + gameeventmanager->FireEvent( event ); + + SetInWarmup( false ); + + ResetScores(); + + // reset the round time + ResetMapTime(); + + State_Transition( STATE_PREROUND ); + } + + void CDODGameRules::SendTeamScoresEvent( void ) + { + // send scores + IGameEvent *event = gameeventmanager->CreateEvent( "dod_team_scores" ); + + if ( event ) + { + CDODTeam *pAllies = GetGlobalDODTeam( TEAM_ALLIES ); + CDODTeam *pAxis = GetGlobalDODTeam( TEAM_AXIS ); + + Assert( pAllies && pAxis ); + + event->SetInt( "allies_caps", pAllies->GetRoundsWon() ); + event->SetInt( "allies_tick", pAllies->GetScore() ); + event->SetInt( "allies_players", pAllies->GetNumPlayers() ); + event->SetInt( "axis_caps", pAxis->GetRoundsWon() ); + event->SetInt( "axis_tick", pAxis->GetScore() ); + event->SetInt( "axis_players", pAxis->GetNumPlayers() ); + + gameeventmanager->FireEvent( event ); + } + } + + void CDODGameRules::State_Think_RESTART( void ) + { + Assert( 0 ); // should never get here, State_Enter_RESTART sets us into a different state + } + + void CDODGameRules::ResetScores( void ) + { + GetGlobalDODTeam( TEAM_ALLIES )->ResetScores(); + GetGlobalDODTeam( TEAM_AXIS )->ResetScores(); + + CDODPlayer *pDODPlayer; + + for( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + pDODPlayer = ToDODPlayer( UTIL_PlayerByIndex( i ) ); + + if (pDODPlayer == NULL) + continue; + + if (FNullEnt( pDODPlayer->edict() )) + continue; + + pDODPlayer->ResetScores(); + } + } + + ConVar dod_showcleanedupents( "dod_showcleanedupents", "0", 0, "Show entities that are removed on round respawn" ); + + // Utility function + bool FindInList( const char **pStrings, const char *pToFind ) + { + int i = 0; + while ( pStrings[i][0] != 0 ) + { + if ( Q_stricmp( pStrings[i], pToFind ) == 0 ) + return true; + i++; + } + + return false; + } + + void CDODGameRules::CleanUpMap() + { + // Recreate all the map entities from the map data (preserving their indices), + // then remove everything else except the players. + + if( dod_showcleanedupents.GetBool() ) + { + Msg( "CleanUpMap\n===============\n" ); + } + + // Get rid of all entities except players. + CBaseEntity *pCur = gEntList.FirstEnt(); + while ( pCur ) + { + if ( !FindInList( s_PreserveEnts, pCur->GetClassname() ) ) + { + if( dod_showcleanedupents.GetBool() ) + { + Msg( "Removed Entity: %s\n", pCur->GetClassname() ); + } + UTIL_Remove( pCur ); + } + + pCur = gEntList.NextEnt( pCur ); + } + + // Really remove the entities so we can have access to their slots below. + gEntList.CleanupDeleteList(); + + // Now reload the map entities. + class CDODMapEntityFilter : public IMapEntityFilter + { + public: + virtual bool ShouldCreateEntity( const char *pClassname ) + { + // Don't recreate the preserved entities. + if ( !FindInList( s_PreserveEnts, pClassname ) ) + { + return true; + } + else + { + // Increment our iterator since it's not going to call CreateNextEntity for this ent. + if ( m_iIterator != g_MapEntityRefs.InvalidIndex() ) + m_iIterator = g_MapEntityRefs.Next( m_iIterator ); + + return false; + } + } + + + virtual CBaseEntity* CreateNextEntity( const char *pClassname ) + { + if ( m_iIterator == g_MapEntityRefs.InvalidIndex() ) + { + // This shouldn't be possible. When we loaded the map, it should have used + // CDODMapLoadEntityFilter, which should have built the g_MapEntityRefs list + // with the same list of entities we're referring to here. + Assert( false ); + return NULL; + } + else + { + CMapEntityRef &ref = g_MapEntityRefs[m_iIterator]; + m_iIterator = g_MapEntityRefs.Next( m_iIterator ); // Seek to the next entity. + + if ( ref.m_iEdict == -1 || engine->PEntityOfEntIndex( ref.m_iEdict ) ) + { + // Doh! The entity was delete and its slot was reused. + // Just use any old edict slot. This case sucks because we lose the baseline. + return CreateEntityByName( pClassname ); + } + else + { + // Cool, the slot where this entity was is free again (most likely, the entity was + // freed above). Now create an entity with this specific index. + return CreateEntityByName( pClassname, ref.m_iEdict ); + } + } + } + + public: + int m_iIterator; // Iterator into g_MapEntityRefs. + }; + CDODMapEntityFilter filter; + filter.m_iIterator = g_MapEntityRefs.Head(); + + // DO NOT CALL SPAWN ON info_node ENTITIES! + + MapEntity_ParseAllEntities( engine->GetMapEntitiesString(), &filter, true ); + } + + int CDODGameRules::CountActivePlayers( void ) + { + int i; + int count = 0; + CDODPlayer *pDODPlayer; + + for (i = 1; i <= gpGlobals->maxClients; i++ ) + { + pDODPlayer = ToDODPlayer( UTIL_PlayerByIndex( i ) ); + + if( pDODPlayer ) + { + if( pDODPlayer->IsReadyToPlay() ) + { + count++; + } + } + } + + return count; + } + + void CDODGameRules::RoundRespawn( void ) + { + CleanUpMap(); + RespawnAllPlayers(); + + // reset per-round scores for each player + for ( int i=0;i<MAX_PLAYERS;i++ ) + { + CDODPlayer *pPlayer = ToDODPlayer( UTIL_PlayerByIndex( i ) ); + + if ( pPlayer ) + { + pPlayer->ResetPerRoundStats(); + } + } + } + + typedef struct { + int iPlayerIndex; + int iScore; + } playerscore_t; + + int PlayerScoreInfoSort( const playerscore_t *p1, const playerscore_t *p2 ) + { + // check frags + if ( p1->iScore > p2->iScore ) + return -1; + if ( p2->iScore > p1->iScore ) + return 1; + + // check index + if ( p1->iPlayerIndex < p2->iPlayerIndex ) + return -1; + + return 1; + } + + // Store which event happened most recently, flag cap or bomb explode + void CDODGameRules::CapEvent( int event, int team ) + { + switch( team ) + { + case TEAM_ALLIES: + m_iLastAlliesCapEvent = event; + break; + case TEAM_AXIS: + m_iLastAxisCapEvent = event; + break; + } + } + + void FillEventCategory( IGameEvent *event, int side, int category, CUtlVector<playerscore_t> &pList ) + { + switch ( side ) + { + case 0: + event->SetInt( "category_left", category ); + break; + case 1: + event->SetInt( "category_right", category ); + break; + } + + static const char *pCategoryNames[2][6] = + { + { + "left_1", + "left_score_1", + "left_2", + "left_score_2", + "left_3", + "left_score_3" + }, + { + "right_1", + "right_score_1", + "right_2", + "right_score_2", + "right_3", + "right_score_3" + } + }; + + int iNumInList = pList.Count(); + + if ( iNumInList > 0 ) + { + event->SetInt( pCategoryNames[side][0], pList[0].iPlayerIndex ); + event->SetInt( pCategoryNames[side][1], pList[0].iScore ); + } + else + event->SetInt( pCategoryNames[side][0], 0 ); + + if ( iNumInList > 1 ) + { + event->SetInt( pCategoryNames[side][2], pList[1].iPlayerIndex ); + event->SetInt( pCategoryNames[side][3], pList[1].iScore ); + } + else + event->SetInt( pCategoryNames[side][2], 0 ); + + if ( iNumInList > 2 ) + { + event->SetInt( pCategoryNames[side][4], pList[2].iPlayerIndex ); + event->SetInt( pCategoryNames[side][5], pList[2].iScore ); + } + else + event->SetInt( pCategoryNames[side][4], 0 ); + } + + //Input for other entities to declare a round winner. + //Most often a dod_control_point_master saying that the + //round timer expired or that someone capped all the flags + void CDODGameRules::SetWinningTeam( int team ) + { + if ( team != TEAM_ALLIES && team != TEAM_AXIS ) + { + Assert( !"bad winning team set" ); + return; + } + + PlayWinSong(team); + + GetGlobalDODTeam( team )->IncrementRoundsWon(); + + switch(team) + { + case TEAM_ALLIES: + { + State_Transition( STATE_ALLIES_WIN ); + } + break; + case TEAM_AXIS: + { + State_Transition( STATE_AXIS_WIN ); + } + break; + default: + break; + } + + IGameEvent *event = gameeventmanager->CreateEvent( "dod_round_win" ); + if ( event ) + { + event->SetInt( "team", team ); + gameeventmanager->FireEvent( event ); + } + + // if this was in colmar, and the losing team did not cap any points, + // the winners may have gotten an achievement + if ( FStrEq( STRING(gpGlobals->mapname), "dod_colmar" ) ) + { + CControlPointMaster *pMaster = dynamic_cast<CControlPointMaster *>( gEntList.FindEntityByClassname( NULL, "dod_control_point_master" ) ); + + if ( pMaster ) + { + // 1. losing team must not own any control points + // 2. for each point that the winning team owns, that takes bombs, it must still have 2 bombs required + bool bFlawlessVictory = true; + + int iNumCP = pMaster->GetNumPoints(); + + for ( int i=0;i<iNumCP;i++ ) + { + CControlPoint *pPoint = pMaster->GetCPByIndex(i); + + if ( !pPoint || !pPoint->PointIsVisible() ) + continue; + + // if the enemy owns any visible points, not a flawless victory + if ( pPoint->GetOwner() != team ) + { + bFlawlessVictory = false; + } + + // 0 bombs remaining means we blew it up and now own it. + // 1 bomb remaining means we own it, but the other team blew it up a bit. + else if ( pPoint->GetBombsRequired() > 0 && pPoint->GetBombsRemaining() == 1 ) + { + bFlawlessVictory = false; + } + } + + if ( bFlawlessVictory ) + { + GetGlobalDODTeam( team )->AwardAchievement( ACHIEVEMENT_DOD_COLMAR_DEFENSE ); + } + } + } + + // send team scores + SendTeamScoresEvent(); + + IGameEvent *winEvent = gameeventmanager->CreateEvent( "dod_win_panel" ); + + if ( winEvent ) + { + // determine what categories to send + + if ( m_bUsingTimer ) + { + if ( team == m_iTimerWinTeam ) + { + // timer expired, defenders win + // show total time that was defended + winEvent->SetBool( "show_timer_defend", true ); + winEvent->SetInt( "timer_time", m_pRoundTimer->GetTimerMaxLength() ); + } + else + { + // attackers win + // show time it took for them to win + winEvent->SetBool( "show_timer_attack", true ); + + int iTimeElapsed = m_pRoundTimer->GetTimerMaxLength() - (int)m_pRoundTimer->GetTimeRemaining(); + winEvent->SetInt( "timer_time", iTimeElapsed ); + } + } + else + { + winEvent->SetBool( "show_timer_attack", false ); + winEvent->SetBool( "show_timer_defend", false ); + } + + int iLastEvent = ( team == TEAM_ALLIES ) ? m_iLastAlliesCapEvent : m_iLastAxisCapEvent; + + winEvent->SetInt( "final_event", iLastEvent ); + + int i; + int index; + + CUtlVector<playerscore_t> m_TopCappers; + CUtlVector<playerscore_t> m_TopDefenders; + CUtlVector<playerscore_t> m_TopBombers; + CUtlVector<playerscore_t> m_TopKills; + + CDODTeam *pWinningTeam = GetGlobalDODTeam( team ); + + int iNumPlayers = pWinningTeam->GetNumPlayers(); + + for ( i=0;i<iNumPlayers;i++ ) + { + CDODPlayer *pPlayer = dynamic_cast<CDODPlayer *>( pWinningTeam->GetPlayer(i) ); + + if ( pPlayer ) + { + int iCaps = pPlayer->GetPerRoundCaps(); + if ( iCaps ) + { + index = m_TopCappers.AddToTail(); + m_TopCappers[index].iPlayerIndex = pPlayer->entindex(); + m_TopCappers[index].iScore = iCaps; + } + + int iDefenses = pPlayer->GetPerRoundDefenses(); + if ( iDefenses ) + { + index = m_TopDefenders.AddToTail(); + m_TopDefenders[index].iPlayerIndex = pPlayer->entindex(); + m_TopDefenders[index].iScore = iDefenses; + } + + // bombs + int iBombsDetonated = pPlayer->GetPerRoundBombsDetonated(); + if ( iBombsDetonated ) + { + index = m_TopBombers.AddToTail(); + m_TopBombers[index].iPlayerIndex = pPlayer->entindex(); + m_TopBombers[index].iScore = iBombsDetonated; + } + + // kills + int iKills = pPlayer->GetPerRoundKills(); + if ( iKills ) + { + index = m_TopKills.AddToTail(); + m_TopKills[index].iPlayerIndex = pPlayer->entindex(); + m_TopKills[index].iScore = iKills; + } + + pPlayer->StatEvent_RoundWin(); + } + } + + CDODTeam *pLosingTeam = GetGlobalDODTeam( ( team == TEAM_ALLIES ) ? TEAM_AXIS : TEAM_ALLIES ); + + iNumPlayers = pLosingTeam->GetNumPlayers(); + + for ( i=0;i<iNumPlayers;i++ ) + { + CDODPlayer *pPlayer = dynamic_cast<CDODPlayer *>( pLosingTeam->GetPlayer(i) ); + + if ( pPlayer ) + { + pPlayer->StatEvent_RoundLoss(); + } + } + + m_TopCappers.Sort( PlayerScoreInfoSort ); + m_TopDefenders.Sort( PlayerScoreInfoSort ); + m_TopBombers.Sort( PlayerScoreInfoSort ); + m_TopKills.Sort( PlayerScoreInfoSort ); + + // Decide what two categories to show in the winpanel + // based on the gametype and which event have good information + // to show + + int iCategoryPriority[8]; + int pos = 0; + + // Default is to show two blank sides + iCategoryPriority[pos++] = WINPANEL_TOP3_NONE; + iCategoryPriority[pos++] = WINPANEL_TOP3_NONE; + + // Only show a category if it has information in it + if ( m_TopKills.Count() > 0 ) + { + iCategoryPriority[pos++] = WINPANEL_TOP3_KILLERS; + } + + if ( m_TopDefenders.Count() > 0 ) + { + iCategoryPriority[pos++] = WINPANEL_TOP3_DEFENDERS; + } + + if ( m_TopBombers.Count() > 0 ) + { + iCategoryPriority[pos++] = WINPANEL_TOP3_BOMBERS; + } + else if ( m_TopCappers.Count() > 0 ) + { + iCategoryPriority[pos++] = WINPANEL_TOP3_CAPPERS; + } + + // Get the two most interesting + int iLeftCategory = iCategoryPriority[pos-1]; + int iRightCategory = iCategoryPriority[pos-2]; + + switch( iLeftCategory ) + { + case WINPANEL_TOP3_BOMBERS: + FillEventCategory( winEvent, 0, iLeftCategory, m_TopBombers ); + break; + case WINPANEL_TOP3_CAPPERS: + FillEventCategory( winEvent, 0, iLeftCategory, m_TopCappers ); + break; + case WINPANEL_TOP3_DEFENDERS: + FillEventCategory( winEvent, 0, iLeftCategory, m_TopDefenders ); + break; + case WINPANEL_TOP3_KILLERS: + FillEventCategory( winEvent, 0, iLeftCategory, m_TopKills ); + break; + case WINPANEL_TOP3_NONE: + default: + break; + } + + switch( iRightCategory ) + { + case WINPANEL_TOP3_BOMBERS: + FillEventCategory( winEvent, 1, iRightCategory, m_TopBombers ); + break; + case WINPANEL_TOP3_CAPPERS: + FillEventCategory( winEvent, 1, iRightCategory, m_TopCappers ); + break; + case WINPANEL_TOP3_DEFENDERS: + FillEventCategory( winEvent, 1, iRightCategory, m_TopDefenders ); + break; + case WINPANEL_TOP3_KILLERS: + FillEventCategory( winEvent, 1, iRightCategory, m_TopKills ); + break; + case WINPANEL_TOP3_NONE: + default: + break; + } + + gameeventmanager->FireEvent( winEvent ); + } + } + + void TestWinpanel( void ) + { + + IGameEvent *event = gameeventmanager->CreateEvent( "dod_round_win" ); + event->SetInt( "team", TEAM_ALLIES ); + + if ( event ) + { + gameeventmanager->FireEvent( event ); + } + + IGameEvent *event2 = gameeventmanager->CreateEvent( "dod_point_captured" ); + if ( event2 ) + { + char cappers[9]; // pCappingPlayers is max length 8 + int i; + for( i=0;i<1;i++ ) + { + cappers[i] = (char)1; + } + + cappers[i] = '\0'; + + // pCappingPlayers is a null terminated list of player indeces + event2->SetString( "cappers", cappers ); + event2->SetBool( "bomb", true ); + + gameeventmanager->FireEvent( event2 ); + } + + IGameEvent *winEvent = gameeventmanager->CreateEvent( "dod_win_panel" ); + + if ( winEvent ) + { + if ( 1 ) + { + if ( 0 /*team == m_iTimerWinTeam */) + { + // timer expired, defenders win + // show total time that was defended + winEvent->SetBool( "show_timer_defend", true ); + winEvent->SetInt( "timer_time", 0 /*m_pRoundTimer->GetTimerMaxLength() */); + } + else + { + // attackers win + // show time it took for them to win + winEvent->SetBool( "show_timer_attack", true ); + + int iTimeElapsed = 90; //m_pRoundTimer->GetTimerMaxLength() - (int)m_pRoundTimer->GetTimeRemaining(); + winEvent->SetInt( "timer_time", iTimeElapsed ); + } + } + else + { + winEvent->SetBool( "show_timer_attack", false ); + winEvent->SetBool( "show_timer_defend", false ); + } + + int iLastEvent = CAP_EVENT_FLAG; + + winEvent->SetInt( "final_event", iLastEvent ); + + CUtlVector<playerscore_t> m_TopKillers; + CUtlVector<playerscore_t> m_TopDefenders; + CUtlVector<playerscore_t> m_TopCappers; + CUtlVector<playerscore_t> m_TopBombers; + + CDODPlayer *pPlayer = ToDODPlayer( UTIL_PlayerByIndex(1) ); + + if ( !pPlayer ) + return; + + int i = 0; + int index; + for ( i=0;i<3;i++ ) + { + index = m_TopCappers.AddToTail(); + m_TopCappers[index].iPlayerIndex = pPlayer->entindex(); + m_TopCappers[index].iScore = pPlayer->GetPerRoundCaps() + 1; + + index = m_TopDefenders.AddToTail(); + m_TopDefenders[index].iPlayerIndex = pPlayer->entindex(); + m_TopDefenders[index].iScore = pPlayer->GetPerRoundDefenses() + 1; + + index = m_TopBombers.AddToTail(); + m_TopBombers[index].iPlayerIndex = pPlayer->entindex(); + m_TopBombers[index].iScore = pPlayer->GetPerRoundBombsDetonated() + 1; + + index = m_TopKillers.AddToTail(); + m_TopKillers[index].iPlayerIndex = pPlayer->entindex(); + m_TopKillers[index].iScore = pPlayer->GetPerRoundKills() + 1; + } + + m_TopCappers.Sort( PlayerScoreInfoSort ); + m_TopDefenders.Sort( PlayerScoreInfoSort ); + + //FillEventCategory( winEvent, 0, WINPANEL_TOP3_KILLERS, m_TopKillers ); + //FillEventCategory( winEvent, 1, WINPANEL_TOP3_DEFENDERS, m_TopDefenders ); + FillEventCategory( winEvent, 0, WINPANEL_TOP3_BOMBERS, m_TopBombers ); + FillEventCategory( winEvent, 1, WINPANEL_TOP3_CAPPERS, m_TopCappers ); + + gameeventmanager->FireEvent( winEvent ); + } + } + ConCommand dod_test_winpanel( "dod_test_winpanel", TestWinpanel, "", FCVAR_CHEAT ); + + // bForceRespawn - respawn player even if dead or dying + // bTeam - if true, only respawn the passed team + // iTeam - team to respawn + void CDODGameRules::RespawnPlayers( bool bForceRespawn, bool bTeam /* = false */, int iTeam/* = TEAM_UNASSIGNED */ ) + { + if ( bTeam ) + { + if ( iTeam == TEAM_ALLIES ) + DevMsg( 2, "Respawning Allies\n" ); + else if ( iTeam == TEAM_AXIS ) + DevMsg( 2, "Respawning Axis\n" ); + else + Assert(!"Trying to respawn a strange team"); + } + + CDODPlayer *pPlayer; + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + pPlayer = ToDODPlayer( UTIL_PlayerByIndex( i ) ); + + if ( !pPlayer ) + continue; + + // Check for team specific spawn + if ( bTeam && pPlayer->GetTeamNumber() != iTeam ) + continue; + + // players that haven't chosen a class can never spawn + if( pPlayer->m_Shared.DesiredPlayerClass() == PLAYERCLASS_UNDEFINED ) + { + ClientPrint(pPlayer, HUD_PRINTTALK, "#game_will_spawn"); + continue; + } + + // If we aren't force respawning, don't respawn players that: + // - are alive + // - are still in the death anim stage of dying + if ( !bForceRespawn ) + { + if ( pPlayer->IsAlive() ) + continue; + + if ( gpGlobals->curtime < ( pPlayer->GetDeathTime() + DEATH_CAM_TIME ) ) + continue; + + if ( pPlayer->GetObserverMode() == OBS_MODE_FREEZECAM ) + continue; + + if ( State_Get() != STATE_PREROUND && pPlayer->State_Get() == STATE_DEATH_ANIM ) + continue; + } + + // Respawn this player + pPlayer->DODRespawn(); + } + } + + bool CDODGameRules::IsPlayerClassOnTeam( int cls, int team ) + { + if( cls == PLAYERCLASS_RANDOM ) + return true; + + CDODTeam *pTeam = GetGlobalDODTeam( team ); + + return ( cls >= 0 && cls < pTeam->GetNumPlayerClasses() ); + } + + bool CDODGameRules::CanPlayerJoinClass( CDODPlayer *pPlayer, int cls ) + { + if( cls == PLAYERCLASS_RANDOM ) + { + return mp_allowrandomclass.GetBool(); + } + + if( ReachedClassLimit( pPlayer->GetTeamNumber(), cls ) ) + return false; + + return true; + } + + bool CDODGameRules::ReachedClassLimit( int team, int cls ) + { + Assert( cls != PLAYERCLASS_UNDEFINED ); + Assert( cls != PLAYERCLASS_RANDOM ); + + // get the cvar + int iClassLimit = GetClassLimit( team, cls ); + + // count how many are active + int iClassExisting = CountPlayerClass( team, cls ); + + CDODTeam *pTeam = GetGlobalDODTeam( team ); + const CDODPlayerClassInfo &pThisClassInfo = pTeam->GetPlayerClassInfo( cls ); + + if( mp_combinemglimits.GetBool() && pThisClassInfo.m_bClassLimitMGMerge ) + { + // find the other classes that have "mergemgclasses" + + for( int i=0; i<pTeam->GetNumPlayerClasses();i++ ) + { + if( i != cls ) + { + const CDODPlayerClassInfo &pClassInfo = pTeam->GetPlayerClassInfo( i ); + if( pClassInfo.m_bClassLimitMGMerge ) + { + // add that class' limits and counts + iClassLimit += GetClassLimit( team, i ); + iClassExisting += CountPlayerClass( team, i ); + } + } + } + } + + if( iClassLimit > -1 && iClassExisting >= iClassLimit ) + { + return true; + } + + return false; + } + + int CDODGameRules::CountPlayerClass( int team, int cls ) + { + int num = 0; + CDODPlayer *pDODPlayer; + + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + pDODPlayer = ToDODPlayer( UTIL_PlayerByIndex( i ) ); + + if (pDODPlayer == NULL) + continue; + + if (FNullEnt( pDODPlayer->edict() )) + continue; + + if( pDODPlayer->GetTeamNumber() != team ) + continue; + + if( pDODPlayer->m_Shared.DesiredPlayerClass() == cls ) + num++; + } + + return num; + } + + int CDODGameRules::GetClassLimit( int team, int cls ) + { + CDODTeam *pTeam = GetGlobalDODTeam( team ); + + Assert( pTeam ); + + const CDODPlayerClassInfo &pClassInfo = pTeam->GetPlayerClassInfo( cls ); + + int iClassLimit; + + ConVar *pLimitCvar = ( ConVar * )cvar->FindVar( pClassInfo.m_szLimitCvar ); + + Assert( pLimitCvar ); + + if( pLimitCvar ) + iClassLimit = pLimitCvar->GetInt(); + else + iClassLimit = -1; + + return iClassLimit; + } + + void CDODGameRules::CheckLevelInitialized() + { + if ( !m_bLevelInitialized ) + { + // Count the number of spawn points for each team + // This determines the maximum number of players allowed on each + + CBaseEntity* ent = NULL; + + m_iSpawnPointCount_Allies = 0; + m_iSpawnPointCount_Axis = 0; + + while ( ( ent = gEntList.FindEntityByClassname( ent, "info_player_allies" ) ) != NULL ) + { + if ( IsSpawnPointValid( ent, NULL ) ) + { + m_iSpawnPointCount_Allies++; + + // store in a list + m_AlliesSpawnPoints.AddToTail( ent ); + } + else + { + Warning("Invalid allies spawnpoint at (%.1f,%.1f,%.1f)\n", + ent->GetAbsOrigin()[0],ent->GetAbsOrigin()[2],ent->GetAbsOrigin()[2] ); + } + } + + while ( ( ent = gEntList.FindEntityByClassname( ent, "info_player_axis" ) ) != NULL ) + { + if ( IsSpawnPointValid( ent, NULL ) ) + { + m_iSpawnPointCount_Axis++; + + // store in a list + m_AxisSpawnPoints.AddToTail( ent ); + } + else + { + Warning("Invalid axis spawnpoint at (%.1f,%.1f,%.1f)\n", + ent->GetAbsOrigin()[0],ent->GetAbsOrigin()[2],ent->GetAbsOrigin()[2] ); + } + } + + m_bLevelInitialized = true; + } + } + + CUtlVector<EHANDLE> *CDODGameRules::GetSpawnPointListForTeam( int iTeam ) + { + switch ( iTeam ) + { + case TEAM_ALLIES: + return &m_AlliesSpawnPoints; + case TEAM_AXIS: + return &m_AxisSpawnPoints; + default: + break; + } + + return NULL; + } + + /* create some proxy entities that we use for transmitting data */ + void CDODGameRules::CreateStandardEntities() + { + // Create the player resource + g_pPlayerResource = (CPlayerResource*)CBaseEntity::Create( "dod_player_manager", vec3_origin, vec3_angle ); + + // Create the objective resource + g_pObjectiveResource = (CDODObjectiveResource *)CBaseEntity::Create( "dod_objective_resource", vec3_origin, vec3_angle ); + + Assert( g_pObjectiveResource ); + + // Create the entity that will send our data to the client. +#ifdef DBGFLAG_ASSERT + CBaseEntity *pEnt = +#endif + CBaseEntity::Create( "dod_gamerules", vec3_origin, vec3_angle ); + Assert( pEnt ); + } + + ConVar dod_waverespawnfactor( "dod_waverespawnfactor", "1.0", FCVAR_REPLICATED | FCVAR_CHEAT, "Factor for respawn wave timers" ); + + float CDODGameRules::GetWaveTime( int iTeam ) + { + float flRespawnTime = 0.0f; + + switch( iTeam ) + { + case TEAM_ALLIES: + flRespawnTime = ( m_iNumAlliesRespawnWaves > 0 ) ? m_AlliesRespawnQueue[m_iAlliesRespawnHead] : -1; + break; + case TEAM_AXIS: + flRespawnTime = ( m_iNumAxisRespawnWaves > 0 ) ? m_AxisRespawnQueue[m_iAxisRespawnHead] : -1; + break; + default: + Assert( !"Why are you trying to get the wave time for a non-team?" ); + break; + } + + return flRespawnTime; + } + + float CDODGameRules::GetMaxWaveTime( int nTeam ) + { + float fTime = 0; + + // Quick waves to get everyone in if we are in PREROUND + if ( State_Get() == STATE_PREROUND ) + { + return 1.0; + } + + int nNumPlayers = GetGlobalDODTeam( nTeam )->GetNumPlayers(); + + if( nNumPlayers < 3 ) + fTime = 6.f; + else if( nNumPlayers < 6 ) + fTime = 8.f; + else if( nNumPlayers < 8 ) + fTime = 10.f; + else if( nNumPlayers < 10 ) + fTime = 11.f; + else if( nNumPlayers < 12 ) + fTime = 12.f; + else if( nNumPlayers < 14 ) + fTime = 13.f; + else + fTime = 14.f; + + //adjust wave time based on mapper settings + //they can adjust the factor ( default 1.0 ) + // to give longer or shorter wait times for + // either team + if( nTeam == TEAM_ALLIES ) + fTime *= m_GamePlayRules.m_fAlliesRespawnFactor; + else if( nTeam == TEAM_AXIS ) + fTime *= m_GamePlayRules.m_fAxisRespawnFactor; + + // Finally, adjust the respawn time based on how well the team is doing + // a team with more flags should respawn faster. + // Give a bonus to respawn time for each flag that we own that we + // don't own by default. + + CControlPointMaster *pMaster = dynamic_cast<CControlPointMaster*>( gEntList.FindEntityByClassname( NULL, "dod_control_point_master" ) ); + + if( pMaster ) + { + int advantageFlags = pMaster->CountAdvantageFlags( nTeam ); + + // this can be negative if we are losing, this will add time! + + fTime -= (float)(advantageFlags) * dod_flagrespawnbonus.GetFloat(); + } + + fTime *= dod_waverespawnfactor.GetFloat(); + + // Minimum 5 seconds + if (fTime <= DEATH_CAM_TIME) + fTime = DEATH_CAM_TIME; + + // Maximum 20 seconds + if ( fTime > MAX_WAVE_RESPAWN_TIME ) + { + fTime = MAX_WAVE_RESPAWN_TIME; + } + + return fTime; + } + + + void CDODGameRules::CreateOrJoinRespawnWave( CDODPlayer *pPlayer ) + { + int team = pPlayer->GetTeamNumber(); + float flWaveTime = GetWaveTime( team ) - gpGlobals->curtime; + + if( flWaveTime <= 0 ) + { + // start a new wave + + DevMsg( "Wave: Starting a new wave for team %d, time %.1f\n", team, GetMaxWaveTime(team) ); + + //start a new wave with this player + AddWaveTime( team, GetMaxWaveTime(team) ); + } + else + { + // see if this player needs to start a new wave + + int team = pPlayer->GetTeamNumber(); + float flSpawnEligibleTime = gpGlobals->curtime + DEATH_CAM_TIME; + + if ( team == TEAM_ALLIES ) + { + bool bFoundWave = false; + + int i = m_iAlliesRespawnHead; + + while( i != m_iAlliesRespawnTail ) + { + // if the player can fit in this wave, set bFound = true + if ( flSpawnEligibleTime < m_AlliesRespawnQueue[i] ) + { + bFoundWave = true; + break; + } + + i = ( i+1 ) % DOD_RESPAWN_QUEUE_SIZE; + } + + if ( !bFoundWave ) + { + // add a new wave to the end + AddWaveTime( TEAM_ALLIES, GetMaxWaveTime(TEAM_ALLIES) ); + } + } + else if ( team == TEAM_AXIS ) + { + bool bFoundWave = false; + + int i = m_iAxisRespawnHead; + + while( i != m_iAxisRespawnTail ) + { + // if the player can fit in this wave, set bFound = true + if ( flSpawnEligibleTime < m_AxisRespawnQueue[i] ) + { + bFoundWave = true; + break; + } + + i = ( i+1 ) % DOD_RESPAWN_QUEUE_SIZE; + } + + if ( !bFoundWave ) + { + // add a new wave to the end + AddWaveTime( TEAM_AXIS, GetMaxWaveTime(TEAM_AXIS) ); + } + } + else + Assert( 0 ); + } + } + + bool CDODGameRules::InRoundRestart( void ) + { + if ( State_Get() == STATE_PREROUND ) + return true; + + return false; + } + + void CDODGameRules::PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info ) + { + CDODPlayer *pDODVictim = ToDODPlayer( pVictim ); + + // if you're still playing dod, you know how this works, let's not + // interfere with the freezecam panel + //bool bPlayed = pDODVictim->HintMessage( HINT_PLAYER_KILLED_WAVETIME ); + + // If we already played the killed hint, play the deathcam hint + //if ( !bPlayed ) + //{ + // pDODVictim->HintMessage( HINT_DEATHCAM ); + //} + + CBaseEntity *pInflictor = info.GetInflictor(); + CBaseEntity *pKiller = info.GetAttacker(); + CDODPlayer *pScorer = ToDODPlayer( GetDeathScorer( pKiller, pInflictor ) ); + + if( pScorer && pScorer->IsPlayer() && pScorer != pVictim ) + { + if( pVictim->GetTeamNumber() == pScorer->GetTeamNumber() ) + { + pScorer->HintMessage( HINT_FRIEND_KILLED, true ); //force this + } + else + { + pScorer->HintMessage( HINT_ENEMY_KILLED ); + } + } + + // determine if this kill affected a nemesis relationship + int iDeathFlags = 0; + if ( pScorer ) + { + CalcDominationAndRevenge( pScorer, pDODVictim, &iDeathFlags ); + } + + pDODVictim->SetDeathFlags( iDeathFlags ); // for deathnotice I assume? + + DeathNotice( pVictim, info ); + + // dvsents2: uncomment when removing all FireTargets + // variant_t value; + // g_EventQueue.AddEvent( "game_playerdie", "Use", value, 0, pVictim, pVictim ); + FireTargets( "game_playerdie", pVictim, pVictim, USE_TOGGLE, 0 ); + + bool bScoring = !IsInWarmup(); + + if( bScoring ) + { + pVictim->IncrementDeathCount( 1 ); + } + + // Did the player kill himself? + if ( pVictim == pScorer ) + { + // Players lose a frag for killing themselves + //if( bScoring ) + // pVictim->IncrementFragCount( -1 ); + } + else if ( pScorer ) + { + // if a player dies in a deathmatch game and the killer is a client, award the killer some points + if( bScoring ) + pScorer->IncrementFragCount( DODPointsForKill( pVictim, info ) ); + + // Allow the scorer to immediately paint a decal + pScorer->AllowImmediateDecalPainting(); + + // dvsents2: uncomment when removing all FireTargets + //variant_t value; + //g_EventQueue.AddEvent( "game_playerkill", "Use", value, 0, pScorer, pScorer ); + FireTargets( "game_playerkill", pScorer, pScorer, USE_TOGGLE, 0 ); + + // see if this saved a capture + if ( pDODVictim->m_signals.GetState() & SIGNAL_CAPTUREAREA ) + { + //find the area the player is in and see if his death causes a block + CAreaCapture *pArea = dynamic_cast<CAreaCapture *>(gEntList.FindEntityByClassname( NULL, "dod_capture_area" ) ); + while( pArea ) + { + if ( pArea->CheckIfDeathCausesBlock( pDODVictim, pScorer ) ) + { + break; + } + + pArea = dynamic_cast<CAreaCapture *>( gEntList.FindEntityByClassname( pArea, "dod_capture_area" ) ); + } + } + if ( pDODVictim->m_bIsDefusing && pDODVictim->m_pDefuseTarget && pScorer->GetTeamNumber() != pDODVictim->GetTeamNumber() ) + { + CDODBombTarget *pTarget = pDODVictim->m_pDefuseTarget; + + pTarget->DefuseBlocked( pScorer ); + + IGameEvent *event = gameeventmanager->CreateEvent( "dod_kill_defuser" ); + if ( event ) + { + event->SetInt( "userid", pScorer->GetUserID() ); + event->SetInt( "victimid", pDODVictim->GetUserID() ); + + gameeventmanager->FireEvent( event ); + } + } + } + else + { + // Players lose a frag for letting the world kill them + //if( bScoring ) + // pVictim->IncrementFragCount( -1 ); + } + } + + void CDODGameRules::DetectGameRules( void ) + { + bool bFound = false; + + CBaseEntity *pEnt = NULL; + + pEnt = gEntList.FindEntityByClassname( pEnt, "info_doddetect" ); + + while( pEnt ) + { + CDODDetect *pDetect = dynamic_cast<CDODDetect *>(pEnt); + + if( pDetect && pDetect->IsMasteredOn() ) + { + CDODGamePlayRules *pRules = pDetect->GetGamePlay(); + Assert( pRules ); + CopyGamePlayLogic( *pRules ); + bFound = true; + break; + } + + pEnt = gEntList.FindEntityByClassname( pEnt, "info_doddetect" ); + } + + if( !bFound ) + { + m_GamePlayRules.Reset(); + } + } + + void CDODGameRules::PlayWinSong( int team ) + { + switch(team) + { + case TEAM_ALLIES: + BroadcastSound( "Game.USWin" ); + break; + case TEAM_AXIS: + BroadcastSound( "Game.GermanWin" ); + break; + default: + Assert(0); + break; + } + } + + void CDODGameRules::BroadcastSound( const char *sound ) + { + //send it to everyone + IGameEvent *event = gameeventmanager->CreateEvent( "dod_broadcast_audio" ); + if ( event ) + { + event->SetString( "sound", sound ); + gameeventmanager->FireEvent( event ); + } + } + + void CDODGameRules::PlayStartRoundVoice( void ) + { + // One for the Allies.. + switch( m_GamePlayRules.m_iAlliesStartRoundVoice ) + { + case STARTROUND_ATTACK: + PlaySpawnSoundToTeam( "Voice.US_ObjectivesAttack", TEAM_ALLIES ); + break; + + case STARTROUND_DEFEND: + PlaySpawnSoundToTeam( "Voice.US_ObjectivesDefend", TEAM_ALLIES ); + break; + + case STARTROUND_BEACH: + PlaySpawnSoundToTeam( "Voice.US_Beach", TEAM_ALLIES ); + break; + + case STARTROUND_ATTACK_TIMED: + PlaySpawnSoundToTeam( "Voice.US_ObjectivesAttackTimed", TEAM_ALLIES ); + break; + + case STARTROUND_DEFEND_TIMED: + PlaySpawnSoundToTeam( "Voice.US_ObjectivesDefendTimed", TEAM_ALLIES ); + break; + + case STARTROUND_FLAGS: + default: + PlaySpawnSoundToTeam( "Voice.US_Flags", TEAM_ALLIES ); + break; + } + + // and one for the Axis + switch( m_GamePlayRules.m_iAxisStartRoundVoice ) + { + case STARTROUND_ATTACK: + PlaySpawnSoundToTeam( "Voice.German_ObjectivesAttack", TEAM_AXIS ); + break; + + case STARTROUND_DEFEND: + PlaySpawnSoundToTeam( "Voice.German_ObjectivesDefend", TEAM_AXIS ); + break; + + case STARTROUND_BEACH: + PlaySpawnSoundToTeam( "Voice.German_Beach", TEAM_AXIS ); + break; + + case STARTROUND_ATTACK_TIMED: + PlaySpawnSoundToTeam( "Voice.German_ObjectivesAttackTimed", TEAM_AXIS ); + break; + + case STARTROUND_DEFEND_TIMED: + PlaySpawnSoundToTeam( "Voice.German_ObjectivesDefendTimed", TEAM_AXIS ); + break; + + case STARTROUND_FLAGS: + default: + PlaySpawnSoundToTeam( "Voice.German_Flags", TEAM_AXIS ); + break; + } + } + + void CDODGameRules::PlaySpawnSoundToTeam( const char *sound, int team ) + { + // find the first valid player and make them do it as a voice command + CDODPlayer *pPlayer; + + static int iLastSpeaker = 1; + + int iCurrent = iLastSpeaker; + + bool bBreakLoop = false; + + while( !bBreakLoop ) + { + iCurrent++; + if( iCurrent > gpGlobals->maxClients ) + iCurrent = 1; + + if( iCurrent == iLastSpeaker ) + { + // couldn't find a different player. check the same player again + // and then break regardless + bBreakLoop = true; + } + + pPlayer = ToDODPlayer( UTIL_PlayerByIndex( iCurrent ) ); + + if (pPlayer == NULL) + continue; + + if (FNullEnt( pPlayer->edict() )) + continue; + + if( pPlayer && pPlayer->GetTeamNumber() == team && pPlayer->IsAlive() ) + { + CPASFilter filter( pPlayer->WorldSpaceCenter() ); + pPlayer->EmitSound( filter, pPlayer->entindex(), sound ); + + pPlayer->DoAnimationEvent( PLAYERANIMEVENT_HANDSIGNAL ); + + iLastSpeaker = iCurrent; + break; + } + } + } + + void CDODGameRules::ClientDisconnected( edict_t *pClient ) + { + CDODPlayer *pPlayer = ToDODPlayer( GetContainingEntity( pClient ) ); + + if( pPlayer ) + { + pPlayer->DestroyRagdoll(); + + pPlayer->StatEvent_UploadStats(); + } + + // Tally the latest time for this player + pPlayer->TallyLatestTimePlayedPerClass( pPlayer->GetTeamNumber(), pPlayer->m_Shared.DesiredPlayerClass() ); + + pPlayer->RemoveNemesisRelationships(); + + for( int j=0;j<7;j++ ) + { + m_flSecondsPlayedPerClass_Allies[j] += pPlayer->m_flTimePlayedPerClass_Allies[j]; + m_flSecondsPlayedPerClass_Axis[j] += pPlayer->m_flTimePlayedPerClass_Axis[j]; + } + + int iPlayerIndex = pPlayer->entindex(); + Assert( iPlayerIndex >= 1 && iPlayerIndex <= MAX_PLAYERS); + if ( iPlayerIndex >= 1 && iPlayerIndex <= MAX_PLAYERS ) + { + // for every other player, set all all the kills with respect to this player to 0 + for ( int i = 1; i <= MAX_PLAYERS; i++ ) + { + CDODPlayer *p = ToDODPlayer( UTIL_PlayerByIndex(i) ); + if ( !p ) + continue; + + p->iNumKilledByUnanswered[iPlayerIndex] = 0; + } + } + + BaseClass::ClientDisconnected( pClient ); + } + + void CDODGameRules::DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info ) + { + // Work out what killed the player, and send a message to all clients about it + const char *killer_weapon_name = "world"; // by default, the player is killed by the world + int killer_ID = 0; + + // Find the killer & the scorer + CBaseEntity *pInflictor = info.GetInflictor(); + CBaseEntity *pKiller = info.GetAttacker(); + CDODPlayer *pScorer = ToDODPlayer( GetDeathScorer( pKiller, pInflictor ) ); + CDODPlayer *pDODVictim = ToDODPlayer( pVictim ); + + Assert( pDODVictim ); + + if ( pScorer ) // Is the killer a client? + { + killer_ID = pScorer->GetUserID(); + + if ( pInflictor ) + { + if ( pInflictor == pScorer ) + { + CWeaponDODBase *pWeapon = pScorer->GetActiveDODWeapon(); + + if ( pWeapon ) + { + int iWeaponType = pWeapon->GetDODWpnData().m_WeaponType; + + // Putting this here because we already have the weapon pointer. + if ( iWeaponType == WPN_TYPE_MG && pScorer->GetTeamNumber() != pVictim->GetTeamNumber() ) + { + CDODBipodWeapon *pMG = dynamic_cast<CDODBipodWeapon *>( pWeapon ); + Assert( pMG ); + if ( pMG->IsDeployed() ) + { + pScorer->HandleDeployedMGKillCount( 1 ); + } + } + + // if the weapon does not belong to the same team + if ( pWeapon->GetDODWpnData().m_iDefaultTeam != pScorer->GetTeamNumber() && + pScorer->GetTeamNumber() != pVictim->GetTeamNumber() ) + { + pScorer->HandleEnemyWeaponsAchievement( 1 ); + } + + // achievement for getting kills with several different weapon types in one life + if ( pScorer->GetTeamNumber() != pVictim->GetTeamNumber() ) + { + pScorer->HandleComboWeaponKill( iWeaponType ); + } + + if( info.GetDamageCustom() & MELEE_DMG_SECONDARYATTACK ) + { + //it was a butt or bayonet! + killer_weapon_name = pWeapon->GetSecondaryDeathNoticeName(); + } + // If the inflictor is the killer, then it must be their current weapon doing the damage + else + { + killer_weapon_name = pWeapon->GetClassname(); + } + } + } + else + { + killer_weapon_name = STRING( pInflictor->m_iClassname ); // it's just that easy + } + } + } + else + { + killer_weapon_name = STRING( pInflictor->m_iClassname ); + } + + // strip the NPC_* or weapon_* from the inflictor's classname + if ( strncmp( killer_weapon_name, "weapon_", 7 ) == 0 ) + { + killer_weapon_name += 7; + } + else if ( strncmp( killer_weapon_name, "func_", 5 ) == 0 ) + { + killer_weapon_name += 5; + } + else if ( strncmp( killer_weapon_name, "rocket_", 7 ) == 0 ) + { + killer_weapon_name += 7; + } + else if ( strncmp( killer_weapon_name, "grenade_", 8 ) == 0 ) + { + killer_weapon_name += 8; + + // achievement for getting kills with several different weapon types in one life + if ( pScorer && pScorer->GetTeamNumber() != pVictim->GetTeamNumber() ) + { + pScorer->HandleComboWeaponKill( WPN_TYPE_GRENADE ); + } + } + + IGameEvent *event = gameeventmanager->CreateEvent( "player_death" ); + + if ( event ) + { + event->SetInt("userid", pVictim->GetUserID() ); + event->SetInt("attacker", killer_ID ); + event->SetString("weapon", killer_weapon_name ); + event->SetInt("priority", 7 ); + + if ( pDODVictim->GetDeathFlags() & DOD_DEATHFLAG_DOMINATION ) + { + event->SetInt( "dominated", 1 ); + } + if ( pDODVictim->GetDeathFlags() & DOD_DEATHFLAG_REVENGE ) + { + event->SetInt( "revenge", 1 ); + } + + gameeventmanager->FireEvent( event ); + } + } + + + // CDODDetect - map entity for mappers to choose game rules + LINK_ENTITY_TO_CLASS( info_doddetect, CDODDetect ); + + CDODDetect::CDODDetect() + { + m_GamePlayRules.Reset(); + } + + void CDODDetect::Spawn( void ) + { + SetSolid( SOLID_NONE ); + + BaseClass::Spawn(); + } + + bool CDODDetect::IsMasteredOn( void ) + { + //For now return true + return true; + } + + bool CDODDetect::KeyValue( const char *szKeyName, const char *szValue ) + { + if (FStrEq(szKeyName, "detect_allies_respawnfactor")) + { + m_GamePlayRules.m_fAlliesRespawnFactor = atof(szValue); + } + else if (FStrEq(szKeyName, "detect_axis_respawnfactor")) + { + m_GamePlayRules.m_fAxisRespawnFactor = atof(szValue); + } + else if (FStrEq(szKeyName, "detect_allies_startroundvoice")) + { + m_GamePlayRules.m_iAlliesStartRoundVoice = atoi(szValue); + } + else if (FStrEq(szKeyName, "detect_axis_startroundvoice")) + { + m_GamePlayRules.m_iAxisStartRoundVoice = atof(szValue); + } + else + return CBaseEntity::KeyValue( szKeyName, szValue ); + + return true; + } + + //checks to see if the desired team is stacked, returns true if it is + bool CDODGameRules::TeamStacked( int iNewTeam, int iCurTeam ) + { + //players are allowed to change to their own team + if(iNewTeam == iCurTeam) + return false; + + int iTeamLimit = mp_limitteams.GetInt(); + + // Tabulate the number of players on each team. + int iNumAllies = GetGlobalTeam( TEAM_ALLIES )->GetNumPlayers(); + int iNumAxis = GetGlobalTeam( TEAM_AXIS )->GetNumPlayers(); + + switch ( iNewTeam ) + { + case TEAM_ALLIES: + if( iCurTeam != TEAM_UNASSIGNED && iCurTeam != TEAM_SPECTATOR ) + { + if((iNumAllies + 1) > (iNumAxis + iTeamLimit - 1)) + return true; + else + return false; + } + else + { + if((iNumAllies + 1) > (iNumAxis + iTeamLimit)) + return true; + else + return false; + } + break; + case TEAM_AXIS: + if( iCurTeam != TEAM_UNASSIGNED && iCurTeam != TEAM_SPECTATOR ) + { + if((iNumAxis + 1) > (iNumAllies + iTeamLimit - 1)) + return true; + else + return false; + } + else + { + if((iNumAxis + 1) > (iNumAllies + iTeamLimit)) + return true; + else + return false; + } + break; + } + + return false; + } + + // Falling damage stuff. + #define DOD_PLAYER_FATAL_FALL_SPEED 900 // approx 60 feet + #define DOD_PLAYER_MAX_SAFE_FALL_SPEED 500 // approx 20 feet + #define DOD_DAMAGE_FOR_FALL_SPEED ((float)100 / ( DOD_PLAYER_FATAL_FALL_SPEED - DOD_PLAYER_MAX_SAFE_FALL_SPEED )) // damage per unit per second. + + /* + #define PLAYER_FALL_PUNCH_THRESHHOLD (float)350 // won't punch player's screen/make scrape noise unless player falling at least this fast. + */ + + float CDODGameRules::FlPlayerFallDamage( CBasePlayer *pPlayer ) + { + pPlayer->m_Local.m_flFallVelocity -= DOD_PLAYER_MAX_SAFE_FALL_SPEED; + return pPlayer->m_Local.m_flFallVelocity * DOD_DAMAGE_FOR_FALL_SPEED; + } + +#endif + +//----------------------------------------------------------------------------- +// Purpose: Init CS ammo definitions +//----------------------------------------------------------------------------- + +// shared ammo definition +// JAY: Trying to make a more physical bullet response +#define BULLET_MASS_GRAINS_TO_LB(grains) (0.002285*(grains)/16.0f) +#define BULLET_MASS_GRAINS_TO_KG(grains) lbs2kg(BULLET_MASS_GRAINS_TO_LB(grains)) + +// exaggerate all of the forces, but use real numbers to keep them consistent +#define BULLET_IMPULSE_EXAGGERATION 1 + +// convert a velocity in ft/sec and a mass in grains to an impulse in kg in/s +#define BULLET_IMPULSE(grains, ftpersec) ((ftpersec)*12*BULLET_MASS_GRAINS_TO_KG(grains)*BULLET_IMPULSE_EXAGGERATION) + +CAmmoDef* GetAmmoDef() +{ + static CAmmoDef def; + static bool bInitted = false; + + if ( !bInitted ) + { + bInitted = true; + + //pistol ammo + def.AddAmmoType( DOD_AMMO_COLT, DMG_BULLET, TRACER_NONE, 0, 0, 21, 5000, 10, 14 ); + def.AddAmmoType( DOD_AMMO_P38, DMG_BULLET, TRACER_NONE, 0, 0, 24, 5000, 10, 14 ); + def.AddAmmoType( DOD_AMMO_C96, DMG_BULLET, TRACER_NONE, 0, 0, 60, 5000, 10, 14 ); + + //rifles + def.AddAmmoType( DOD_AMMO_GARAND, DMG_BULLET, TRACER_NONE, 0, 0, 88, 9000, 10, 14 ); + def.AddAmmoType( DOD_AMMO_K98, DMG_BULLET, TRACER_NONE, 0, 0, 65, 9000, 10, 14 ); + def.AddAmmoType( DOD_AMMO_M1CARBINE, DMG_BULLET, TRACER_NONE, 0, 0, 165, 9000, 10, 14 ); + def.AddAmmoType( DOD_AMMO_SPRING, DMG_BULLET, TRACER_NONE, 0, 0, 55, 9000, 10, 14 ); + + //submg + def.AddAmmoType( DOD_AMMO_SUBMG, DMG_BULLET, TRACER_NONE, 0, 0, 210, 7000, 10, 14 ); + def.AddAmmoType( DOD_AMMO_BAR, DMG_BULLET, TRACER_LINE_AND_WHIZ, 0, 0, 260, 9000, 10, 14 ); + + //mg + def.AddAmmoType( DOD_AMMO_30CAL, DMG_BULLET | DMG_MACHINEGUN, TRACER_LINE_AND_WHIZ, 0, 0, 300, 9000, 10, 14 ); + def.AddAmmoType( DOD_AMMO_MG42, DMG_BULLET | DMG_MACHINEGUN, TRACER_LINE_AND_WHIZ, 0, 0, 500, 9000, 10, 14 ); + + //rockets + def.AddAmmoType( DOD_AMMO_ROCKET, DMG_BLAST, TRACER_NONE, 0, 0, 5, 9000, 10, 14 ); + + //grenades + def.AddAmmoType( DOD_AMMO_HANDGRENADE, DMG_BLAST, TRACER_NONE, 0, 0, 2, 1, 4, 8 ); + def.AddAmmoType( DOD_AMMO_STICKGRENADE, DMG_BLAST, TRACER_NONE, 0, 0, 2, 1, 4, 8 ); + def.AddAmmoType( DOD_AMMO_HANDGRENADE_EX, DMG_BLAST, TRACER_NONE, 0, 0, 1, 1, 4, 8 ); + def.AddAmmoType( DOD_AMMO_STICKGRENADE_EX, DMG_BLAST, TRACER_NONE, 0, 0, 1, 1, 4, 8 ); + + // smoke grenades + def.AddAmmoType( DOD_AMMO_SMOKEGRENADE_US, DMG_BLAST, TRACER_NONE, 0, 0, 2, 1, 4, 8 ); + def.AddAmmoType( DOD_AMMO_SMOKEGRENADE_GER, DMG_BLAST, TRACER_NONE, 0, 0, 2, 1, 4, 8 ); + def.AddAmmoType( DOD_AMMO_SMOKEGRENADE_US_LIVE, DMG_BLAST, TRACER_NONE, 0, 0, 2, 1, 4, 8 ); + def.AddAmmoType( DOD_AMMO_SMOKEGRENADE_GER_LIVE,DMG_BLAST, TRACER_NONE, 0, 0, 2, 1, 4, 8 ); + + // rifle grenades + def.AddAmmoType( DOD_AMMO_RIFLEGRENADE_US, DMG_BLAST, TRACER_NONE, 0, 0, 2, 1, 4, 8 ); + def.AddAmmoType( DOD_AMMO_RIFLEGRENADE_GER, DMG_BLAST, TRACER_NONE, 0, 0, 2, 1, 4, 8 ); + def.AddAmmoType( DOD_AMMO_RIFLEGRENADE_US_LIVE, DMG_BLAST, TRACER_NONE, 0, 0, 2, 1, 4, 8 ); + def.AddAmmoType( DOD_AMMO_RIFLEGRENADE_GER_LIVE,DMG_BLAST, TRACER_NONE, 0, 0, 2, 1, 4, 8 ); + } + + return &def; +} + +#ifndef CLIENT_DLL +void CDODGameRules::AddWaveTime( int team, float flTime ) +{ + switch ( team ) + { + case TEAM_ALLIES: + { + Assert( m_iNumAlliesRespawnWaves < DOD_RESPAWN_QUEUE_SIZE ); + + if ( m_iNumAlliesRespawnWaves >= DOD_RESPAWN_QUEUE_SIZE ) + { + Warning( "Trying to add too many allies respawn waves\n" ); + return; + } + + m_AlliesRespawnQueue.Set( m_iAlliesRespawnTail, gpGlobals->curtime + flTime ); + m_iNumAlliesRespawnWaves++; + + m_iAlliesRespawnTail = ( m_iAlliesRespawnTail + 1 ) % DOD_RESPAWN_QUEUE_SIZE; + + DevMsg( 1, "AddWaveTime ALLIES head %d tail %d numtotal %d time %.1f\n", + m_iAlliesRespawnHead.Get(), + m_iAlliesRespawnTail.Get(), + m_iNumAlliesRespawnWaves, + gpGlobals->curtime + flTime ); + } + break; + case TEAM_AXIS: + { + Assert( m_iNumAxisRespawnWaves < DOD_RESPAWN_QUEUE_SIZE ); + + if ( m_iNumAxisRespawnWaves >= DOD_RESPAWN_QUEUE_SIZE ) + { + Warning( "Trying to add too many axis respawn waves\n" ); + return; + } + + m_AxisRespawnQueue.Set( m_iAxisRespawnTail, gpGlobals->curtime + flTime ); + m_iNumAxisRespawnWaves++; + + m_iAxisRespawnTail = ( m_iAxisRespawnTail + 1 ) % DOD_RESPAWN_QUEUE_SIZE; + + DevMsg( 1, "AddWaveTime AXIS head %d tail %d numtotal %d time %.1f\n", + m_iAxisRespawnHead.Get(), + m_iAxisRespawnTail.Get(), + m_iNumAxisRespawnWaves, + gpGlobals->curtime + flTime ); + } + break; + default: + Assert(0); + break; + } +} + +void CDODGameRules::PopWaveTime( int team ) +{ + switch ( team ) + { + case TEAM_ALLIES: + { + Assert( m_iNumAlliesRespawnWaves > 0 ); + + m_iAlliesRespawnHead = ( m_iAlliesRespawnHead + 1 ) % DOD_RESPAWN_QUEUE_SIZE; + m_iNumAlliesRespawnWaves--; + + DevMsg( 1, "PopWaveTime ALLIES head %d tail %d numtotal %d time %.1f\n", + m_iAlliesRespawnHead.Get(), + m_iAlliesRespawnTail.Get(), + m_iNumAlliesRespawnWaves, + gpGlobals->curtime ); + } + break; + case TEAM_AXIS: + { + Assert( m_iNumAxisRespawnWaves > 0 ); + + m_iAxisRespawnHead = ( m_iAxisRespawnHead + 1 ) % DOD_RESPAWN_QUEUE_SIZE; + m_iNumAxisRespawnWaves--; + + DevMsg( 1, "PopWaveTime AXIS head %d tail %d numtotal %d time %.1f\n", + m_iAxisRespawnHead.Get(), + m_iAxisRespawnTail.Get(), + m_iNumAxisRespawnWaves, + gpGlobals->curtime ); + } + break; + default: + Assert(0); + break; + } +} + +#endif + + +#ifndef CLIENT_DLL + +const char *CDODGameRules::GetChatPrefix( bool bTeamOnly, CBasePlayer *pPlayer ) +{ + char *pszPrefix = ""; + + if ( !pPlayer ) // dedicated server output + { + pszPrefix = ""; + } + else + { + // don't show dead prefix if in the bonus round or at round end + // because we can chat at these times. + bool bShowDeadPrefix = ( pPlayer->IsAlive() == false ) && !IsInBonusRound() && + ( State_Get() != STATE_GAME_OVER ); + + if ( pPlayer->GetTeamNumber() == TEAM_SPECTATOR ) + { + return ""; + } + + if ( bTeamOnly ) + { + if ( bShowDeadPrefix ) + { + pszPrefix = "(Dead)(Team)"; //#chatprefix_deadteam"; + } + else + { + //MATTTODO: localize chat prefixes + pszPrefix = "(Team)"; //"#chatprefix_team"; + } + } + // everyone + else + { + if ( bShowDeadPrefix ) + { + pszPrefix = "(Dead)"; //"#chatprefix_dead"; + } + } + } + + return pszPrefix; +} + +void CDODGameRules::ClientSettingsChanged( CBasePlayer *pPlayer ) +{ + CDODPlayer *pDODPlayer = ToDODPlayer( pPlayer ); + + Assert( pDODPlayer ); + + pDODPlayer->SetAutoReload( Q_atoi( engine->GetClientConVarValue( pPlayer->entindex(), "cl_autoreload" ) ) > 0 ); + pDODPlayer->SetShowHints( Q_atoi( engine->GetClientConVarValue( pPlayer->entindex(), "cl_showhelp" ) ) > 0 ); + pDODPlayer->SetAutoRezoom( Q_atoi( engine->GetClientConVarValue( pPlayer->entindex(), "cl_autorezoom" ) ) > 0 ); + + BaseClass::ClientSettingsChanged( pPlayer ); +} + +//----------------------------------------------------------------------------- +// Purpose: Determines if attacker and victim have gotten domination or revenge +//----------------------------------------------------------------------------- +void CDODGameRules::CalcDominationAndRevenge( CDODPlayer *pAttacker, CDODPlayer *pVictim, int *piDeathFlags ) +{ + // team kills don't count + if ( pAttacker->GetTeamNumber() == pVictim->GetTeamNumber() ) + return; + + int iKillsUnanswered = ++(pVictim->iNumKilledByUnanswered[pAttacker->entindex()]); + + pAttacker->iNumKilledByUnanswered[pVictim->entindex()] = 0; + + if ( DOD_KILLS_DOMINATION == iKillsUnanswered ) + { + // this is the Nth unanswered kill between killer and victim, killer is now dominating victim + *piDeathFlags |= DOD_DEATHFLAG_DOMINATION; + + // set victim to be dominated by killer + pAttacker->m_Shared.SetPlayerDominated( pVictim, true ); + + pAttacker->StatEvent_ScoredDomination(); + } + else if ( pVictim->m_Shared.IsPlayerDominated( pAttacker->entindex() ) ) + { + // the killer killed someone who was dominating him, gains revenge + *piDeathFlags |= DOD_DEATHFLAG_REVENGE; + + // set victim to no longer be dominating the killer + pVictim->m_Shared.SetPlayerDominated( pAttacker, false ); + + pAttacker->StatEvent_ScoredRevenge(); + } +} + +int CDODGameRules::DODPointsForKill( CBasePlayer *pVictim, const CTakeDamageInfo &info ) +{ + if ( IsInWarmup() ) + return 0; + + CBaseEntity *pInflictor = info.GetInflictor(); + CBaseEntity *pKiller = info.GetAttacker(); + CDODPlayer *pScorer = ToDODPlayer( GetDeathScorer( pKiller, pInflictor ) ); + + // Don't give -1 points for killing a teammate with the bomb. + // It was their fault for standing too close really. + if ( pVictim->GetTeamNumber() == pScorer->GetTeamNumber() && + info.GetDamageType() & DMG_BOMB ) + { + return 0; + } + + return BaseClass::IPointsForKill( pScorer, pVictim ); + +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the weapon in the player's inventory that would be better than +// the given weapon. +// Note, this version allows us to switch to a weapon that has no ammo as a last +// resort. +//----------------------------------------------------------------------------- +CBaseCombatWeapon *CDODGameRules::GetNextBestWeapon( CBaseCombatCharacter *pPlayer, CBaseCombatWeapon *pCurrentWeapon ) +{ + CBaseCombatWeapon *pCheck; + CBaseCombatWeapon *pBest;// this will be used in the event that we don't find a weapon in the same category. + + int iCurrentWeight = -1; + int iBestWeight = -1;// no weapon lower than -1 can be autoswitched to + pBest = NULL; + + // If I have a weapon, make sure I'm allowed to holster it + if ( pCurrentWeapon ) + { + if ( !pCurrentWeapon->AllowsAutoSwitchFrom() || !pCurrentWeapon->CanHolster() ) + { + // Either this weapon doesn't allow autoswitching away from it or I + // can't put this weapon away right now, so I can't switch. + return NULL; + } + + iCurrentWeight = pCurrentWeapon->GetWeight(); + } + + for ( int i = 0 ; i < pPlayer->WeaponCount(); ++i ) + { + pCheck = pPlayer->GetWeapon( i ); + if ( !pCheck ) + continue; + + // If we have an active weapon and this weapon doesn't allow autoswitching away + // from another weapon, skip it. + if ( pCurrentWeapon && !pCheck->AllowsAutoSwitchTo() ) + continue; + + int iWeight = pCheck->GetWeight(); + + // Empty weapons are lowest priority + if ( !pCheck->HasAnyAmmo() ) + { + iWeight = 0; + } + + if ( iWeight > -1 && iWeight == iCurrentWeight && pCheck != pCurrentWeapon ) + { + // this weapon is from the same category. + if ( pPlayer->Weapon_CanSwitchTo( pCheck ) ) + { + return pCheck; + } + } + else if ( iWeight > iBestWeight && pCheck != pCurrentWeapon )// don't reselect the weapon we're trying to get rid of + { + //Msg( "Considering %s\n", STRING( pCheck->GetClassname() ); + // we keep updating the 'best' weapon just in case we can't find a weapon of the same weight + // that the player was using. This will end up leaving the player with his heaviest-weighted + // weapon. + + // if this weapon is useable, flag it as the best + iBestWeight = pCheck->GetWeight(); + pBest = pCheck; + } + } + + // if we make it here, we've checked all the weapons and found no useable + // weapon in the same catagory as the current weapon. + + // if pBest is null, we didn't find ANYTHING. Shouldn't be possible- should always + // at least get the crowbar, but ya never know. + return pBest; +} + +char *szHitgroupNames[] = +{ + "generic", + "head", + "chest", + "stomach", + "arm_left", + "arm_right", + "leg_left", + "leg_right" +}; + +void CDODGameRules::WriteStatsFile( const char *pszLogName ) +{ + int i, j, k; + + FileHandle_t hFile = filesystem->Open( pszLogName, "w" ); + if ( hFile == FILESYSTEM_INVALID_HANDLE ) + { + Warning( "Helper_LoadFile: missing %s\n", pszLogName ); + return; + } + + // Header + filesystem->FPrintf( hFile, "<?xml version=\"1.0\" ?>\n\n" ); + + // open stats + filesystem->FPrintf( hFile, "<stats>\n" ); + + // per player + for ( i=0;i<MAX_PLAYERS;i++ ) + { + CDODPlayer *pPlayer = ToDODPlayer( UTIL_PlayerByIndex( i ) ); + + if ( pPlayer ) + { + filesystem->FPrintf( hFile, "\t<player>\n" ); + + filesystem->FPrintf( hFile, "\t\t<playername>%s</playername>", pPlayer->GetPlayerName() ); + filesystem->FPrintf( hFile, "\t\t<steamid>STEAM:0:01</steamid>" ); + + //float flTimePlayed = gpGlobals->curtime - pPlayer->m_flConnectTime; + //filesystem->FPrintf( hFile, "\t\t<time_played>%.1f</time_played>\n", flTimePlayed ); + + /* + pPlayer->TallyLatestTimePlayedPerClass( pPlayer->GetTeamNumber(), pPlayer->m_Shared.DesiredPlayerClass() ); + + filesystem->FPrintf( hFile, "\t\t<time_played_per_class>\n" ); + for( j=0;j<7;j++ ) + { + // TODO : add real class names + filesystem->FPrintf( hFile, "\t\t\t<class%i>%.1f</class%i>\n", + j, + pPlayer->m_flTimePlayedPerClass[j], + j ); + } + filesystem->FPrintf( hFile, "\t\t</time_played_per_class>\n" ); + */ + + filesystem->FPrintf( hFile, "\t\t<area_captures>%i</area_captures>\n", pPlayer->m_iNumAreaCaptures ); + filesystem->FPrintf( hFile, "\t\t<area_defenses>%i</area_defenses>\n", pPlayer->m_iNumAreaDefenses ); + filesystem->FPrintf( hFile, "\t\t<bonus_round_kills>%i</bonus_round_kills>\n", pPlayer->m_iNumBonusRoundKills ); + + for ( j=0;j<MAX_WEAPONS;j++ ) + { + if ( pPlayer->m_WeaponStats[j].m_iNumShotsTaken > 0 ) + { + filesystem->FPrintf( hFile, "\t\t<weapon>\n" ); + + // weapon id + // weapon name + + filesystem->FPrintf( hFile, "\t\t\t<weaponname>%s</weaponname>\n", WeaponIDToAlias( j ) ); + filesystem->FPrintf( hFile, "\t\t\t<weaponid>%i</weaponid>\n", j ); + filesystem->FPrintf( hFile, "\t\t\t<shots>%i</shots>\n", pPlayer->m_WeaponStats[j].m_iNumShotsTaken ); + filesystem->FPrintf( hFile, "\t\t\t<hits>%i</hits>\n", pPlayer->m_WeaponStats[j].m_iNumShotsHit ); + filesystem->FPrintf( hFile, "\t\t\t<damage>%i</damage>\n", pPlayer->m_WeaponStats[j].m_iTotalDamageGiven ); + filesystem->FPrintf( hFile, "\t\t\t<avgdist>%.1f</avgdist>\n", pPlayer->m_WeaponStats[j].m_flAverageHitDistance ); + filesystem->FPrintf( hFile, "\t\t\t<kills>%i</kills>\n", pPlayer->m_WeaponStats[j].m_iNumKills ); + + filesystem->FPrintf( hFile, "\t\t\t<hitgroups_hit>\n" ); + for( k=0;k<8;k++ ) + { + if ( pPlayer->m_WeaponStats[j].m_iBodygroupsHit[k] > 0 ) + { + filesystem->FPrintf( hFile, "\t\t\t\t<%s>%i</%s>\n", + szHitgroupNames[k], + pPlayer->m_WeaponStats[j].m_iBodygroupsHit[k], + szHitgroupNames[k] ); + } + } + filesystem->FPrintf( hFile, "\t\t\t</hitgroups_hit>\n" ); + + filesystem->FPrintf( hFile, "\t\t\t<times_hit>%i</times_hit>\n", pPlayer->m_WeaponStats[j].m_iNumHitsTaken ); + filesystem->FPrintf( hFile, "\t\t\t<damage_taken>%i</damage_taken>\n", pPlayer->m_WeaponStats[j].m_iTotalDamageTaken ); + filesystem->FPrintf( hFile, "\t\t\t<times_killed>%i</times_killed>\n", pPlayer->m_WeaponStats[j].m_iTimesKilled ); + + filesystem->FPrintf( hFile, "\t\t\t<hit_in_hitgroups>\n" ); + for( k=0;k<8;k++ ) + { + if ( pPlayer->m_WeaponStats[j].m_iHitInBodygroups[k] > 0 ) + { + filesystem->FPrintf( hFile, "\t\t\t\t<%s>%i</%s>\n", + szHitgroupNames[k], + pPlayer->m_WeaponStats[j].m_iHitInBodygroups[k], + szHitgroupNames[k] ); + } + } + + filesystem->FPrintf( hFile, "\t\t\t</hit_in_hitgroups>\n" ); + + filesystem->FPrintf( hFile, "\t\t</weapon>\n" ); + } + } + + int numKilled = pPlayer->m_KilledPlayers.Count(); + for ( j=0;j<numKilled;j++ ) + { + filesystem->FPrintf( hFile, "<victim>\n" ); + filesystem->FPrintf( hFile, "\t<name>%s</name>\n", pPlayer->m_KilledPlayers[j].m_szPlayerName ); + filesystem->FPrintf( hFile, "\t<userid>%i</userid>\n", pPlayer->m_KilledPlayers[j].m_iUserID ); + filesystem->FPrintf( hFile, "\t<kills>%i</kills>\n", pPlayer->m_KilledPlayers[j].m_iKills ); + filesystem->FPrintf( hFile, "\t<damage>%i</damage>\n", pPlayer->m_KilledPlayers[j].m_iTotalDamage ); + filesystem->FPrintf( hFile, "</victim>" ); + } + + int numAttackers = pPlayer->m_KilledByPlayers.Count(); + for ( j=0;j<numAttackers;j++ ) + { + filesystem->FPrintf( hFile, "<attacker>" ); + filesystem->FPrintf( hFile, "\t<name>%s</name>\n", pPlayer->m_KilledByPlayers[j].m_szPlayerName ); + filesystem->FPrintf( hFile, "\t<userid>%i</userid>\n", pPlayer->m_KilledByPlayers[j].m_iUserID ); + filesystem->FPrintf( hFile, "\t<kills>%i</kills>\n", pPlayer->m_KilledByPlayers[j].m_iKills ); + filesystem->FPrintf( hFile, "\t<damage>%i</damage>\n", pPlayer->m_KilledByPlayers[j].m_iTotalDamage ); + filesystem->FPrintf( hFile, "</attacker>" ); + } + + filesystem->FPrintf( hFile, "\t</player>\n" ); + } + } + + // close stats + filesystem->FPrintf( hFile, "</stats>\n" ); + + filesystem->Close( hFile ); +} + +#include "dod_basegrenade.h" + +//========================================================== +// Called on physics entities that the player +uses ( if sv_turbophysics is on ) +// Here we want to exclude grenades +//========================================================== +bool CDODGameRules::CanEntityBeUsePushed( CBaseEntity *pEnt ) +{ + CDODBaseGrenade *pGrenade = dynamic_cast<CDODBaseGrenade *>( pEnt ); + + if ( pGrenade ) + { + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Engine asks for the list of convars that should tag the server +//----------------------------------------------------------------------------- +void CDODGameRules::GetTaggedConVarList( KeyValues *pCvarTagList ) +{ + BaseClass::GetTaggedConVarList( pCvarTagList ); + + KeyValues *pKV = new KeyValues( "tag" ); + pKV->SetString( "convar", "mp_fadetoblack" ); + pKV->SetString( "tag", "fadetoblack" ); + + pCvarTagList->AddSubKey( pKV ); +} + +#endif //indef CLIENT_DLL + +#ifdef CLIENT_DLL + +void CDODGameRules::SetRoundState( int iRoundState ) +{ + m_iRoundState = iRoundState; + + m_flLastRoundStateChangeTime = gpGlobals->curtime; +} + +#endif // CLIENT_DLL + +bool CDODGameRules::IsBombingTeam( int team ) +{ + if ( team == TEAM_ALLIES ) + return m_bAlliesAreBombing; + + if ( team == TEAM_AXIS ) + return m_bAxisAreBombing; + + return false; +} + +bool CDODGameRules::IsConnectedUserInfoChangeAllowed( CBasePlayer *pPlayer ) +{ +#ifdef GAME_DLL + if( pPlayer ) + { + int iPlayerTeam = pPlayer->GetTeamNumber(); + if( ( iPlayerTeam == TEAM_ALLIES ) || ( iPlayerTeam == TEAM_AXIS ) ) + return false; + } +#else + int iLocalPlayerTeam = GetLocalPlayerTeam(); + if( ( iLocalPlayerTeam == TEAM_ALLIES ) || ( iLocalPlayerTeam == TEAM_AXIS ) ) + return false; +#endif + + return true; +} + + +#ifndef CLIENT_DLL + +ConVar dod_winter_never_drop_presents( "dod_winter_never_drop_presents", "0", FCVAR_CHEAT ); +ConVar dod_winter_always_drop_presents( "dod_winter_always_drop_presents", "0", FCVAR_CHEAT ); +ConVar dod_winter_present_drop_chance( "dod_winter_present_drop_chance", "0.2", FCVAR_CHEAT, "", true, 0.0, true, 1.0 ); + +float CDODGameRules::GetPresentDropChance( void ) +{ + if ( dod_winter_never_drop_presents.GetBool() ) + { + return 0.0; + } + + if ( dod_winter_always_drop_presents.GetBool() ) + { + return 1.0; + } + + if ( m_bWinterHolidayActive ) + { + return dod_winter_present_drop_chance.GetFloat(); + } + + return 0.0; +} + +#endif + +//========================= + +#ifndef CLIENT_DLL + +class CFuncTeamWall : public CBaseEntity +{ + DECLARE_DATADESC(); + DECLARE_CLASS( CFuncTeamWall, CBaseEntity ); + +public: + virtual void Spawn(); + virtual bool KeyValue( const char *szKeyName, const char *szValue ) ; + virtual bool ShouldCollide( int collisionGroup, int contentsMask ) const; + + void WallTouch( CBaseEntity *pOther ); + + void DrawThink( void ); + +private: + Vector m_vecMaxs; + Vector m_vecMins; + + int m_iBlockTeam; + + float m_flNextHintTime; + + bool m_bShowWarning; +}; + +BEGIN_DATADESC( CFuncTeamWall ) + + DEFINE_KEYFIELD( m_iBlockTeam, FIELD_INTEGER, "blockteam" ), + + DEFINE_KEYFIELD( m_vecMaxs, FIELD_VECTOR, "maxs" ), + DEFINE_KEYFIELD( m_vecMins, FIELD_VECTOR, "mins" ), + + DEFINE_KEYFIELD( m_bShowWarning, FIELD_BOOLEAN, "warn" ), + + DEFINE_THINKFUNC( DrawThink ), + + DEFINE_FUNCTION( WallTouch ), + +END_DATADESC() + +LINK_ENTITY_TO_CLASS( func_team_wall, CFuncTeamWall ); + +ConCommand cc_Load_Blocker_Walls( "load_enttext", Load_EntText, 0, FCVAR_CHEAT ); + +ConVar showblockerwalls( "showblockerwalls", "0", FCVAR_CHEAT, "Set to 1 to visualize blocker walls" ); + +void CFuncTeamWall::Spawn( void ) +{ + SetMoveType( MOVETYPE_PUSH ); // so it doesn't get pushed by anything + SetModel( STRING( GetModelName() ) ); + AddEffects( EF_NODRAW ); + SetSolid( SOLID_BBOX ); + + // set our custom collision if we declared this ent through the .ent file + if ( m_vecMins != vec3_origin && m_vecMaxs != vec3_origin ) + { + SetCollisionBounds( m_vecMins, m_vecMaxs ); + + // If we delcared an angle in the .ent file, make us OBB + if ( GetAbsAngles() != vec3_angle ) + { + SetSolid( SOLID_OBB ); + } + } + + SetThink( &CFuncTeamWall::DrawThink ); + SetNextThink( gpGlobals->curtime + 0.1 ); + + SetTouch( &CFuncTeamWall::WallTouch ); + m_flNextHintTime = gpGlobals->curtime; +} + +//----------------------------------------------------------------------------- +// Parse data from a map file +//----------------------------------------------------------------------------- +bool CFuncTeamWall::KeyValue( const char *szKeyName, const char *szValue ) +{ + if ( FStrEq( szKeyName, "mins" )) + { + UTIL_StringToVector( m_vecMins.Base(), szValue ); + return true; + } + + if ( FStrEq( szKeyName, "maxs" )) + { + UTIL_StringToVector( m_vecMaxs.Base(), szValue ); + return true; + } + + if ( FStrEq( szKeyName, "warn" )) + { + m_bShowWarning = atoi(szValue) > 0; + } + + return BaseClass::KeyValue( szKeyName, szValue ); +} + +bool CFuncTeamWall::ShouldCollide( int collisionGroup, int contentsMask ) const +{ + bool bShouldCollide = false; + + if ( collisionGroup == COLLISION_GROUP_PLAYER_MOVEMENT ) + { + switch ( m_iBlockTeam ) + { + case TEAM_UNASSIGNED: + bShouldCollide = ( contentsMask & ( CONTENTS_TEAM1 | CONTENTS_TEAM2 ) ) > 0; + break; + + case TEAM_ALLIES: + bShouldCollide = ( contentsMask & CONTENTS_TEAM1 ) > 0; + break; + + case TEAM_AXIS: + bShouldCollide = ( contentsMask & CONTENTS_TEAM2 ) > 0; + break; + + default: + break; + } + } + + return bShouldCollide; +} + +void CFuncTeamWall::WallTouch( CBaseEntity *pOther ) +{ + if ( !m_bShowWarning ) + return; + + if ( pOther && pOther->IsPlayer() ) + { + CDODPlayer *pPlayer = ToDODPlayer( pOther ); + + if ( pPlayer->GetTeamNumber() == m_iBlockTeam ) + { + // show a "go away" icon + if ( m_flNextHintTime < gpGlobals->curtime ) + { + pPlayer->HintMessage( "#dod_wrong_way" ); + + // global timer, but not critical to keep timer per player. + m_flNextHintTime = gpGlobals->curtime + 1.0; + } + } + } +} + +void CFuncTeamWall::DrawThink( void ) +{ + if ( showblockerwalls.GetBool() ) + { + NDebugOverlay::EntityBounds( this, 255, 0, 0, 0, 0.2 ); + } + + SetNextThink( gpGlobals->curtime + 0.1 ); +} + +#endif + + +#ifdef GAME_DLL + + #include "modelentities.h" + + #define SF_TEAM_WALL_NO_HINT (1<<1) + + //----------------------------------------------------------------------------- + // Purpose: Visualizes a respawn room to the enemy team + //----------------------------------------------------------------------------- + class CFuncNewTeamWall : public CFuncBrush + { + DECLARE_CLASS( CFuncNewTeamWall, CFuncBrush ); + public: + DECLARE_DATADESC(); + DECLARE_SERVERCLASS(); + + virtual void Spawn( void ); + + virtual int UpdateTransmitState( void ); + virtual int ShouldTransmit( const CCheckTransmitInfo *pInfo ); + virtual bool ShouldCollide( int collisionGroup, int contentsMask ) const; + + void WallTouch( CBaseEntity *pOther ); + + void SetActive( bool bActive ); + + private: + float m_flNextHintTime; + }; + + //=========================================================================================================== + + LINK_ENTITY_TO_CLASS( func_teamblocker, CFuncNewTeamWall ); + + BEGIN_DATADESC( CFuncNewTeamWall ) + END_DATADESC() + + IMPLEMENT_SERVERCLASS_ST( CFuncNewTeamWall, DT_FuncNewTeamWall ) + END_SEND_TABLE() + + //----------------------------------------------------------------------------- + // Purpose: + //----------------------------------------------------------------------------- + void CFuncNewTeamWall::Spawn( void ) + { + BaseClass::Spawn(); + + SetActive( true ); + + SetCollisionGroup( DOD_COLLISIONGROUP_BLOCKERWALL ); + + if ( FBitSet( m_spawnflags, SF_TEAM_WALL_NO_HINT ) == false ) + { + SetTouch( &CFuncNewTeamWall::WallTouch ); + m_flNextHintTime = gpGlobals->curtime; + } + } + + //----------------------------------------------------------------------------- + // Purpose: + //----------------------------------------------------------------------------- + int CFuncNewTeamWall::UpdateTransmitState() + { + return SetTransmitState( FL_EDICT_ALWAYS ); + } + + //----------------------------------------------------------------------------- + // Purpose: Only transmit this entity to clients that aren't in our team + //----------------------------------------------------------------------------- + int CFuncNewTeamWall::ShouldTransmit( const CCheckTransmitInfo *pInfo ) + { + return FL_EDICT_ALWAYS; + } + + //----------------------------------------------------------------------------- + // Purpose: + //----------------------------------------------------------------------------- + void CFuncNewTeamWall::SetActive( bool bActive ) + { + if ( bActive ) + { + // We're a trigger, but we want to be solid. Out ShouldCollide() will make + // us non-solid to members of the team that spawns here. + RemoveSolidFlags( FSOLID_TRIGGER ); + RemoveSolidFlags( FSOLID_NOT_SOLID ); + } + else + { + AddSolidFlags( FSOLID_NOT_SOLID ); + AddSolidFlags( FSOLID_TRIGGER ); + } + } + + //----------------------------------------------------------------------------- + // Purpose: + //----------------------------------------------------------------------------- + bool CFuncNewTeamWall::ShouldCollide( int collisionGroup, int contentsMask ) const + { + if ( GetTeamNumber() == TEAM_UNASSIGNED ) + return false; + + if ( collisionGroup == COLLISION_GROUP_PLAYER_MOVEMENT ) + { + switch( GetTeamNumber() ) + { + case TEAM_ALLIES: + if ( !(contentsMask & CONTENTS_TEAM1) ) + return false; + break; + + case TEAM_AXIS: + if ( !(contentsMask & CONTENTS_TEAM2) ) + return false; + break; + } + + return true; + } + + return false; + } + + void CFuncNewTeamWall::WallTouch( CBaseEntity *pOther ) + { + //if ( !m_bShowWarning ) + // return; + + if ( pOther && pOther->IsPlayer() ) + { + CDODPlayer *pPlayer = ToDODPlayer( pOther ); + + if ( pPlayer->GetTeamNumber() == GetTeamNumber() ) + { + // show a "go away" icon + if ( m_flNextHintTime < gpGlobals->curtime ) + { + pPlayer->HintMessage( "#dod_wrong_way" ); + + // global timer, but not critical to keep timer per player. + m_flNextHintTime = gpGlobals->curtime + 1.0; + } + } + } + } + +#else + + //----------------------------------------------------------------------------- + // Purpose: + //----------------------------------------------------------------------------- + class C_FuncNewTeamWall : public C_BaseEntity + { + DECLARE_CLASS( C_FuncNewTeamWall, C_BaseEntity ); + public: + DECLARE_CLIENTCLASS(); + + virtual bool ShouldCollide( int collisionGroup, int contentsMask ) const; + + virtual int DrawModel( int flags ); + }; + + IMPLEMENT_CLIENTCLASS_DT( C_FuncNewTeamWall, DT_FuncNewTeamWall, CFuncNewTeamWall ) + END_RECV_TABLE() + + //----------------------------------------------------------------------------- + // Purpose: + //----------------------------------------------------------------------------- + bool C_FuncNewTeamWall::ShouldCollide( int collisionGroup, int contentsMask ) const + { + if ( GetTeamNumber() == TEAM_UNASSIGNED ) + return false; + + if ( collisionGroup == COLLISION_GROUP_PLAYER_MOVEMENT ) + { + switch( GetTeamNumber() ) + { + case TEAM_ALLIES: + if ( !(contentsMask & CONTENTS_TEAM1) ) + return false; + break; + + case TEAM_AXIS: + if ( !(contentsMask & CONTENTS_TEAM2) ) + return false; + break; + } + + return true; + } + + return false; + } + + int C_FuncNewTeamWall::DrawModel( int flags ) + { + return 1; + } + +#endif diff --git a/game/shared/dod/dod_gamerules.h b/game/shared/dod/dod_gamerules.h new file mode 100644 index 0000000..2649fd8 --- /dev/null +++ b/game/shared/dod/dod_gamerules.h @@ -0,0 +1,526 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: The TF Game rules object +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef DOD_GAMERULES_H +#define DOD_GAMERULES_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "teamplay_gamerules.h" +#include "convar.h" +#include "dod_shareddefs.h" +#include "gamevars_shared.h" +#include "weapon_dodbase.h" +#include "dod_round_timer.h" + +#ifdef CLIENT_DLL + #include "c_baseplayer.h" +#else + #include "player.h" + #include "dod_player.h" + #include "utlqueue.h" + #include "playerclass_info_parse.h" + #include "voice_gamemgr.h" + #include "dod_gamestats.h" +#endif + +#ifdef CLIENT_DLL + #define CDODGameRules C_DODGameRules + #define CDODGameRulesProxy C_DODGameRulesProxy +#else + extern IVoiceGameMgrHelper *g_pVoiceGameMgrHelper; + extern IUploadGameStats *gamestatsuploader; +#endif + +#ifndef CLIENT_DLL + + class CSpawnPoint : public CPointEntity + { + public: + bool IsDisabled() { return m_bDisabled; } + void InputEnable( inputdata_t &inputdata ) { m_bDisabled = false; } + void InputDisable( inputdata_t &inputdata ) { m_bDisabled = true; } + + private: + bool m_bDisabled; + DECLARE_DATADESC(); + }; + +#endif + +class CDODGameRulesProxy : public CGameRulesProxy +{ +public: + DECLARE_CLASS( CDODGameRulesProxy, CGameRulesProxy ); + DECLARE_NETWORKCLASS(); +}; + +class CDODGameRules; + +class CDODRoundStateInfo +{ +public: + DODRoundState m_iRoundState; + const char *m_pStateName; + + void (CDODGameRules::*pfnEnterState)(); // Init and deinit the state. + void (CDODGameRules::*pfnLeaveState)(); + void (CDODGameRules::*pfnThink)(); // Do a PreThink() in this state. +}; + +typedef enum +{ + STARTROUND_ATTACK = 0, + STARTROUND_DEFEND, + + STARTROUND_BEACH, + + STARTROUND_ATTACK_TIMED, + STARTROUND_DEFEND_TIMED, + + STARTROUND_FLAGS, +} startround_voice_t; + +class CDODGamePlayRules +{ +public: + DECLARE_CLASS_NOBASE( CDODGamePlayRules ); + DECLARE_EMBEDDED_NETWORKVAR(); + + DECLARE_SIMPLE_DATADESC(); + + CDODGamePlayRules() + { + Reset(); + } + + // This virtual method is necessary to generate a vtable in all cases + // (DECLARE_PREDICTABLE will generate a vtable also)! + virtual ~CDODGamePlayRules() {} + + void Reset( void ) + { + //RespawnFactor + m_fAlliesRespawnFactor = 1.0f; + m_fAxisRespawnFactor = 1.0f; + } + + //Respawn Factors + float m_fAlliesRespawnFactor; //How delayed are respawning players + float m_fAxisRespawnFactor; //1.0 is normal, 2.0 is twice as long + + int m_iAlliesStartRoundVoice; // Which voice to play at round start + int m_iAxisStartRoundVoice; +}; + + +//Mapper interface for gamerules +class CDODDetect : public CBaseEntity +{ +public: + DECLARE_CLASS( CDODDetect, CBaseEntity ); + + CDODDetect(); + void Spawn( void ); + virtual bool KeyValue( const char *szKeyName, const char *szValue ); + + bool IsMasteredOn( void ); + + inline CDODGamePlayRules *GetGamePlay() { return &m_GamePlayRules; } + CDODGamePlayRules m_GamePlayRules; + +private: +// string_t m_sMaster; +}; + +class CDODViewVectors : public CViewVectors +{ +public: + CDODViewVectors( + Vector vView, + Vector vHullMin, + Vector vHullMax, + Vector vDuckHullMin, + Vector vDuckHullMax, + Vector vDuckView, + Vector vObsHullMin, + Vector vObsHullMax, + Vector vDeadViewHeight, + Vector vProneHullMin, + Vector vProneHullMax ) : + CViewVectors( + vView, + vHullMin, + vHullMax, + vDuckHullMin, + vDuckHullMax, + vDuckView, + vObsHullMin, + vObsHullMax, + vDeadViewHeight ) + { + m_vProneHullMin = vProneHullMin; + m_vProneHullMax = vProneHullMax; + } + + Vector m_vProneHullMin; + Vector m_vProneHullMax; +}; + +//GAMERULES +class CDODGameRules : public CTeamplayRules +{ +public: + DECLARE_CLASS( CDODGameRules, CTeamplayRules ); + + virtual bool ShouldCollide( int collisionGroup0, int collisionGroup1 ); + + inline DODRoundState State_Get( void ) { return m_iRoundState; } + + int GetSubTeam( int team ); + + bool IsGameUnderTimeLimit( void ); + int GetTimeLeft( void ); + + int GetReinforcementTimerSeconds( int team, float flSpawnEligibleTime ); + + bool IsFriendlyFireOn( void ); + bool IsInBonusRound( void ); + + // Get the view vectors for this mod. + virtual const CViewVectors* GetViewVectors() const; + virtual const CDODViewVectors *GetDODViewVectors() const; + virtual const unsigned char *GetEncryptionKey( void ) { return (unsigned char *)"Wl0u5B3F"; } + + bool AwaitingReadyRestart( void ) { return m_bAwaitingReadyRestart; } + float GetRoundRestartTime( void ) { return m_flRestartRoundTime; } + + bool IsInWarmup( void ) { return m_bInWarmup; } + + bool IsBombingTeam( int team ); + + virtual bool IsConnectedUserInfoChangeAllowed( CBasePlayer *pPlayer ); + +#ifndef CLIENT_DLL + float GetPresentDropChance( void ); // holiday 2011, presents instead of ammo boxes +#endif + +#ifdef CLIENT_DLL + + DECLARE_CLIENTCLASS_NOBASE(); // This makes datatables able to access our private vars. + + void SetRoundState( int iRoundState ); + float m_flLastRoundStateChangeTime; + +#else + + DECLARE_SERVERCLASS_NOBASE(); // This makes datatables able to access our private vars. + + CDODGameRules(); + virtual ~CDODGameRules(); + + virtual void LevelShutdown( void ); + void UploadLevelStats( void ); + + virtual bool ClientCommand( CBaseEntity *pEdict, const CCommand &args ); + virtual void RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrc, float flRadius, int iClassIgnore, CBaseEntity *pEntityIgnore ); + virtual void RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore, CBaseEntity *pEntityIgnore, bool bIgnoreWorld = false ); + void RadiusStun( const CTakeDamageInfo &info, const Vector &vecSrc, float flRadius ); + virtual void Think(); + virtual void PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info ); + virtual void ClientDisconnected( edict_t *pClient ); + virtual float FlPlayerFallDamage( CBasePlayer *pPlayer ); + + virtual const char *GetGameDescription( void ) + { + return "Day of Defeat: Source"; + } + + void CreateStandardEntities( void ); + + virtual const char *GetChatPrefix( bool bTeamOnly, CBasePlayer *pPlayer ); + + CBaseEntity *GetPlayerSpawnSpot( CBasePlayer *pPlayer ); + bool IsSpawnPointValid( CBaseEntity *pSpot, CBasePlayer *pPlayer ); + virtual void PlayerSpawn( CBasePlayer *pPlayer ); + + int DODPointsForKill( CBasePlayer *pVictim, const CTakeDamageInfo &info ); + + //Round state machine + void State_Transition( DODRoundState newState ); + void State_Enter( DODRoundState newState ); // Initialize the new state. + void State_Leave(); // Cleanup the previous state. + void State_Think(); // Update the current state. + + CDODRoundStateInfo *m_pCurStateInfo; //Fn ptrs for the current state + float m_flStateTransitionTime; //Timer for round states + + // Find the state info for the specified state. + static CDODRoundStateInfo* State_LookupInfo( DODRoundState state ); + + //State Functions + void State_Enter_INIT( void ); + void State_Think_INIT( void ); + + void State_Enter_PREGAME( void ); + void State_Think_PREGAME( void ); + + void State_Enter_STARTGAME( void ); + void State_Think_STARTGAME( void ); + + void State_Enter_PREROUND( void ); + void State_Think_PREROUND( void ); + + void State_Enter_RND_RUNNING( void ); + void State_Think_RND_RUNNING( void ); + + void State_Enter_ALLIES_WIN( void ); + void State_Think_ALLIES_WIN( void ); + + void State_Enter_AXIS_WIN( void ); + void State_Think_AXIS_WIN( void ); + + void State_Enter_RESTART( void ); + void State_Think_RESTART( void ); + + void SetInWarmup( bool bWarmup ); + void CheckWarmup( void ); + void CheckRestartRound( void ); + void CheckRespawnWaves( void ); + + void InitTeams( void ); + + void RoundRespawn( void ); + void CleanUpMap( void ); + void ResetScores( void ); + + // Respawn everyone regardless of state - round reset + inline void RespawnAllPlayers( void ) { RespawnPlayers( true ); } + + // Respawn only one team, players that are ready to spawn - wave reset + inline void RespawnTeam( int iTeam ) { RespawnPlayers( false, true, iTeam ); } + + void RespawnPlayers( bool bForceRespawn, bool bTeam = false, int iTeam = TEAM_UNASSIGNED ); + + void FailSafeSpawnPlayersOnTeam( int iTeam ); + + bool IsPlayerClassOnTeam( int cls, int team ); + bool CanPlayerJoinClass( CDODPlayer *pPlayer, int cls ); + void ChooseRandomClass( CDODPlayer *pPlayer ); + bool ReachedClassLimit( int team, int cls ); + int CountPlayerClass( int team, int cls ); + int GetClassLimit( int team, int cls ); + + int CountActivePlayers( void ); //How many players have chosen a team? + + void SetWinningTeam( int team ); + void PlayWinSong( int team ); + void PlayStartRoundVoice( void ); + void BroadcastSound( const char *sound ); + void PlaySpawnSoundToTeam( const char *sound, int team ); + + int SelectDefaultTeam( void ); + + void CopyGamePlayLogic( const CDODGamePlayRules otherGamePlay ); + + + void DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info ); + + virtual bool CanHavePlayerItem( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon ); + + bool TeamFull( int team_id ); + bool TeamStacked( int iNewTeam, int iCurTeam ); + + const char *GetPlayerClassName( int cls, int team ); + + virtual void ClientSettingsChanged( CBasePlayer *pPlayer ); + + void CheckChatForReadySignal( CDODPlayer *pPlayer, const char *chatmsg ); + bool AreAlliesReady( void ) { return m_bHeardAlliesReady; } + bool AreAxisReady( void ) { return m_bHeardAxisReady; } + + void CreateOrJoinRespawnWave( CDODPlayer *pPlayer ); + + virtual bool InRoundRestart( void ); + + void SendTeamScoresEvent( void ); + + void WriteStatsFile( const char *pszLogName ); + + void AddTimerSeconds( int iSecondsToAdd ); + int GetTimerSeconds( void ); + + void CapEvent( int event, int team ); + int m_iLastAlliesCapEvent; + int m_iLastAxisCapEvent; + + // Set the time at which the map was reset to 'now' + // and send an event with the time remaining until map change + void ResetMapTime( void ); + + virtual CBaseCombatWeapon *GetNextBestWeapon( CBaseCombatCharacter *pPlayer, CBaseCombatWeapon *pCurrentWeapon ); + + virtual bool CanEntityBeUsePushed( CBaseEntity *pEnt ); + + virtual void CalcDominationAndRevenge( CDODPlayer *pAttacker, CDODPlayer *pVictim, int *piDeathFlags ); + + float m_flNextFailSafeWaveCheckTime; + + CUtlVector<EHANDLE> *GetSpawnPointListForTeam( int iTeam ); + + virtual void GetTaggedConVarList( KeyValues *pCvarTagList ); + +protected: + virtual void GoToIntermission( void ); + virtual bool UseSuicidePenalty() { return false; } + + void CheckPlayerPositions( void ); + +private: + bool CheckTimeLimit( void ); + bool CheckWinLimit( void ); + + void RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore, bool bIgnoreWorld ); + float GetExplosionDamageAdjustment(Vector & vecSrc, Vector & vecEnd, CBaseEntity *pTarget, CBaseEntity *pEntityToIgnore); // returns multiplier between 0.0 and 1.0 that is the percentage of any damage done from vecSrc to vecEnd that actually makes it. + float GetAmountOfEntityVisible(Vector & src, CBaseEntity *pTarget, CBaseEntity *pEntityToIgnore); // returns a value from 0 to 1 that is the percentage of player visible from src. + + void CheckLevelInitialized( void ); + bool m_bLevelInitialized; + + int m_iSpawnPointCount_Allies; //number of allies spawns on the map + int m_iSpawnPointCount_Axis; //number of axis spawns on the map + + #define MAX_PLAYERCLASSES_PER_TEAM 16 + + PLAYERCLASS_FILE_INFO_HANDLE m_hPlayerClassInfoHandles[2][MAX_PLAYERCLASSES_PER_TEAM]; + + // restart and warmup variables + float m_flWarmupTimeEnds; + float m_flNextPeriodicThink; + + Vector2D m_vecPlayerPositions[MAX_PLAYERS]; + + + //BELOW HERE NEED TO BE HOOKED UP + + + int m_iNumAlliesAlive; //the number of players alive on each team + int m_iNumAxisAlive; + int m_iNumAlliesOnTeam; //the number of players on each team + int m_iNumAxisOnTeam; + + bool m_bClanMatch; + bool m_bClanMatchActive; + + float GetMaxWaveTime( int iTeam ); + float GetWaveTime( int iTeam ); + void AddWaveTime( int team, float flTime ); + void PopWaveTime( int team ); + + void DetectGameRules( void ); + + bool m_bHeardAlliesReady; + bool m_bHeardAxisReady; + + bool m_bUsingTimer; + int m_iTimerWinTeam; + CHandle< CDODRoundTimer > m_pRoundTimer; + + bool m_bPlayTimerWarning_1Minute; + bool m_bPlayTimerWarning_2Minute; + + bool m_bInitialSpawn; // first time activating? longer wait time for people to join + + bool m_bChangeLevelOnRoundEnd; + +#endif //CLIENT_DLL + + CNetworkVarEmbedded( CDODGamePlayRules, m_GamePlayRules ); + + CNetworkVar( DODRoundState, m_iRoundState ); + + #define DOD_RESPAWN_QUEUE_SIZE 10 + + CNetworkArray( float, m_AlliesRespawnQueue, DOD_RESPAWN_QUEUE_SIZE ); + CNetworkArray( float, m_AxisRespawnQueue, DOD_RESPAWN_QUEUE_SIZE ); + + CNetworkVar( int, m_iAlliesRespawnHead ); + CNetworkVar( int, m_iAlliesRespawnTail ); + + CNetworkVar( int, m_iAxisRespawnHead ); + CNetworkVar( int, m_iAxisRespawnTail ); + + int m_iNumAlliesRespawnWaves; + int m_iNumAxisRespawnWaves; + + CNetworkVar( bool, m_bInWarmup ); + CNetworkVar( bool, m_bAwaitingReadyRestart ); + CNetworkVar( float, m_flRestartRoundTime ); + CNetworkVar( float, m_flMapResetTime ); // time that the map was reset + + CNetworkVar( bool, m_bAlliesAreBombing ); + CNetworkVar( bool, m_bAxisAreBombing ); + +#ifndef CLIENT_DLL +public: + // Stats + void Stats_PlayerKill( int team, int cls ); + void Stats_PlayerCap( int team, int cls ); + void Stats_PlayerDefended( int team, int cls ); + void Stats_WeaponFired( int weaponID ); + void Stats_WeaponHit( int weaponID, float flDist ); + int Stats_WeaponDistanceToBucket( int weaponID, float flDist ); + + float m_flSecondsPlayedPerClass_Allies[7]; + float m_flSecondsPlayedPerClass_Axis[7]; + + int m_iStatsKillsPerClass_Allies[6]; + int m_iStatsKillsPerClass_Axis[6]; + + int m_iStatsSpawnsPerClass_Allies[6]; + int m_iStatsSpawnsPerClass_Axis[6]; + + int m_iStatsCapsPerClass_Allies[6]; + int m_iStatsCapsPerClass_Axis[6]; + + int m_iStatsDefensesPerClass_Allies[6]; + int m_iStatsDefensesPerClass_Axis[6]; + + int m_iWeaponShotsFired[WEAPON_MAX]; + int m_iWeaponShotsHit[WEAPON_MAX]; + int m_iWeaponDistanceBuckets[WEAPON_MAX][DOD_NUM_WEAPON_DISTANCE_BUCKETS]; // distances of buckets are defined per-weapon + + // List of spawn points + CUtlVector<EHANDLE> m_AlliesSpawnPoints; + CUtlVector<EHANDLE> m_AxisSpawnPoints; + + bool m_bWinterHolidayActive; + +#endif // ndef CLIENTDLL +}; + +//----------------------------------------------------------------------------- +// Gets us at the team fortress game rules +//----------------------------------------------------------------------------- + +inline CDODGameRules* DODGameRules() +{ + return static_cast<CDODGameRules*>(g_pGameRules); +} + +#ifdef CLIENT_DLL + +#else + bool EntityPlacementTest( CBaseEntity *pMainEnt, const Vector &vOrigin, Vector &outPos, bool bDropToGround ); +#endif //CLIENT_DLL + + +#endif // DOD_GAMERULES_H diff --git a/game/shared/dod/dod_player_shared.cpp b/game/shared/dod/dod_player_shared.cpp new file mode 100644 index 0000000..ec35acf --- /dev/null +++ b/game/shared/dod/dod_player_shared.cpp @@ -0,0 +1,1396 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "dod_gamerules.h" +#include "takedamageinfo.h" +#include "dod_shareddefs.h" +#include "effect_dispatch_data.h" + +#include "weapon_dodbase.h" +#include "weapon_dodbipodgun.h" +#include "weapon_dodbaserpg.h" +#include "weapon_dodsniper.h" + +#include "movevars_shared.h" +#include "engine/IEngineSound.h" +#include "SoundEmitterSystem/isoundemittersystembase.h" +#include "engine/ivdebugoverlay.h" +#include "obstacle_pushaway.h" +#include "props_shared.h" + +#include "decals.h" +#include "util_shared.h" + +#ifdef CLIENT_DLL + + #include "c_dod_player.h" + #include "prediction.h" + #include "clientmode_dod.h" + #include "vgui_controls/AnimationController.h" + + #define CRecipientFilter C_RecipientFilter + +#else + + #include "dod_player.h" + +#endif + +ConVar dod_bonusround( "dod_bonusround", "1", FCVAR_REPLICATED, "If true, the winners of the round can attack in the intermission." ); +ConVar sv_showimpacts("sv_showimpacts", "0", FCVAR_REPLICATED | FCVAR_CHEAT, "Shows client (red) and server (blue) bullet impact point" ); + +void DispatchEffect( const char *pName, const CEffectData &data ); + +bool CDODPlayer::CanMove( void ) const +{ + bool bValidMoveState = (State_Get() == STATE_ACTIVE || State_Get() == STATE_OBSERVER_MODE); + + if ( !bValidMoveState ) + { + return false; + } + + return true; +} + +// BUG! This is not called on the client at respawn, only first spawn! +void CDODPlayer::SharedSpawn() +{ + BaseClass::SharedSpawn(); + + // Reset the animation state or we will animate to standing + // when we spawn + + m_Shared.SetJumping( false ); + + m_flMinNextStepSoundTime = gpGlobals->curtime; + + m_bPlayingProneMoveSound = false; +} + +float GetDensityFromMaterial( surfacedata_t *pSurfaceData ) +{ + float flMaterialMod = 1.0f; + + Assert( pSurfaceData ); + + // material mod is how many points of damage it costs to go through + // 1 unit of the material + + switch( pSurfaceData->game.material ) + { + //super soft +// case CHAR_TEX_LEAVES: +// flMaterialMod = 1.2f; +// break; + + case CHAR_TEX_FLESH: + flMaterialMod = 1.35f; + break; + + //soft +// case CHAR_TEX_STUCCO: +// case CHAR_TEX_SNOW: + case CHAR_TEX_GLASS: + case CHAR_TEX_WOOD: + case CHAR_TEX_TILE: + flMaterialMod = 1.8f; + break; + + //hard +// case CHAR_TEX_SKY: +// case CHAR_TEX_ROCK: +// case CHAR_TEX_SAND: + case CHAR_TEX_CONCRETE: + case CHAR_TEX_DIRT: // "sand" + flMaterialMod = 6.6f; + break; + + //really hard +// case CHAR_TEX_HEAVYMETAL: + case CHAR_TEX_GRATE: + case CHAR_TEX_METAL: + flMaterialMod = 13.5f; + break; + + case 'X': // invisible collision material + flMaterialMod = 0.1f; + break; + + //medium +// case CHAR_TEX_BRICK: +// case CHAR_TEX_GRAVEL: +// case CHAR_TEX_GRASS: + default: + +#ifndef CLIENT_DLL + AssertMsg( 0, UTIL_VarArgs( "Material has unknown materialmod - '%c' \n", pSurfaceData->game.material ) ); +#endif + + flMaterialMod = 5.0f; + break; + } + + Assert( flMaterialMod > 0 ); + + return flMaterialMod; +} + +static bool TraceToExit( const Vector &start, + const Vector &dir, + Vector &end, + const float flStepSize, + const float flMaxDistance ) +{ + float flDistance = 0; + Vector last = start; + + while ( flDistance < flMaxDistance ) + { + flDistance += flStepSize; + + // no point in tracing past the max distance. + // if this check fails, we save ourselves a traceline later + if ( flDistance > flMaxDistance ) + { + flDistance = flMaxDistance; + } + + end = start + flDistance * dir; + + // point contents fails to return proper contents inside a func_detail brush, eg the dod_flash + // stairs + + //int contents = UTIL_PointContents( end ); + + trace_t tr; + UTIL_TraceLine( end, end, MASK_SOLID | CONTENTS_HITBOX, NULL, &tr ); + + //if ( (UTIL_PointContents ( end ) & MASK_SOLID) == 0 ) + + if ( !tr.startsolid ) + { + // found first free point + return true; + } + } + + return false; +} + +#include "ammodef.h" + +#define NEW_HITBOX_GROUP_CODE 1 +#undef ARM_PENETRATION + +#ifndef CLIENT_DLL +#include "ndebugoverlay.h" +#endif +void CDODPlayer::FireBullets( const FireBulletsInfo_t &info ) +{ + trace_t tr; + trace_t reverseTr; //Used to find exit points + static int iMaxPenetrations = 6; + int iPenetrations = 0; + float flDamage = info.m_flDamage; //Remaining damage in the bullet + Vector vecSrc = info.m_vecSrc; + Vector vecEnd = vecSrc + info.m_vecDirShooting * info.m_flDistance; + + static int iTraceMask = ( ( MASK_SOLID | CONTENTS_DEBRIS | CONTENTS_HITBOX | CONTENTS_PRONE_HELPER ) & ~CONTENTS_GRATE ); + + CBaseEntity *pLastHitEntity = this; // start with us so we don't trace ourselves + + int iDamageType = GetAmmoDef()->DamageType( info.m_iAmmoType ); + int iCollisionGroup = COLLISION_GROUP_NONE; + +#ifdef GAME_DLL + int iNumHeadshots = 0; +#endif + + while ( flDamage > 0 && iPenetrations < iMaxPenetrations ) + { + //DevMsg( 2, "penetration: %d, starting dmg: %.1f\n", iPenetrations, flDamage ); + + CBaseEntity *pPreviousHit = pLastHitEntity; + + // skip the shooter always + CTraceFilterSkipTwoEntities ignoreShooterAndPrevious( this, pPreviousHit, iCollisionGroup ); + UTIL_TraceLine( vecSrc, vecEnd, iTraceMask, &ignoreShooterAndPrevious, &tr ); + + const float rayExtension = 40.0f; + UTIL_ClipTraceToPlayers( vecSrc, vecEnd + info.m_vecDirShooting * rayExtension, iTraceMask, &ignoreShooterAndPrevious, &tr ); + + if ( tr.fraction == 1.0f ) + break; // we didn't hit anything, stop tracing shoot + + // New hitbox code that uses hitbox groups instead of trying to trace + // through the player + if ( tr.m_pEnt && tr.m_pEnt->IsPlayer() ) + { + switch( tr.hitgroup ) + { +#ifdef GAME_DLL + case HITGROUP_HEAD: + { + if ( tr.m_pEnt->GetTeamNumber() != GetTeamNumber() ) + { + iNumHeadshots++; + } + } + break; +#endif + + case HITGROUP_LEFTARM: + case HITGROUP_RIGHTARM: + { + //DevMsg( 2, "Hit arms, tracing against alt hitboxes.. \n" ); + + CDODPlayer *pPlayer = ToDODPlayer( tr.m_pEnt ); + + // set hitbox set to "dod_no_arms" + pPlayer->SetHitboxSet( 1 ); + + trace_t newTr; + + // re-fire the trace + UTIL_TraceLine( vecSrc, vecEnd, iTraceMask, &ignoreShooterAndPrevious, &newTr ); + + // if we hit the same player in the chest + if ( tr.m_pEnt == newTr.m_pEnt ) + { + //DevMsg( 2, ".. and we hit the chest.\n" ); + + Assert( tr.hitgroup != newTr.hitgroup ); // If we hit this, hitbox sets are broken + + // use that damage instead + tr = newTr; + } + + // set hitboxes back to "dod" + pPlayer->SetHitboxSet( 0 ); + } + break; + + default: + break; + } + } + + pLastHitEntity = tr.m_pEnt; + + if ( sv_showimpacts.GetBool() ) + { +#ifdef CLIENT_DLL + // draw red client impact markers + debugoverlay->AddBoxOverlay( tr.endpos, Vector(-1,-1,-1), Vector(1,1,1), QAngle(0,0,0), 255, 0, 0, 127, 4 ); + + if ( tr.m_pEnt && tr.m_pEnt->IsPlayer() ) + { + C_BasePlayer *player = ToBasePlayer( tr.m_pEnt ); + player->DrawClientHitboxes( 4, true ); + } +#else + // draw blue server impact markers + NDebugOverlay::Box( tr.endpos, Vector(-1,-1,-1), Vector(1,1,1), 0,0,255,127, 4 ); + + if ( tr.m_pEnt && tr.m_pEnt->IsPlayer() ) + { + CBasePlayer *player = ToBasePlayer( tr.m_pEnt ); + player->DrawServerHitboxes( 4, true ); + } +#endif + } + +#ifdef CLIENT_DLL + // See if the bullet ended up underwater + started out of the water + if ( enginetrace->GetPointContents( tr.endpos ) & (CONTENTS_WATER|CONTENTS_SLIME) ) + { + trace_t waterTrace; + UTIL_TraceLine( vecSrc, tr.endpos, (MASK_SHOT|CONTENTS_WATER|CONTENTS_SLIME), this, iCollisionGroup, &waterTrace ); + + if( waterTrace.allsolid != 1 ) + { + CEffectData data; + data.m_vOrigin = waterTrace.endpos; + data.m_vNormal = waterTrace.plane.normal; + data.m_flScale = random->RandomFloat( 8, 12 ); + + if ( waterTrace.contents & CONTENTS_SLIME ) + { + data.m_fFlags |= FX_WATER_IN_SLIME; + } + + DispatchEffect( "gunshotsplash", data ); + } + } + else + { + //Do Regular hit effects + + // Don't decal nodraw surfaces + if ( !( tr.surface.flags & (SURF_SKY|SURF_NODRAW|SURF_HINT|SURF_SKIP) ) ) + { + CBaseEntity *pEntity = tr.m_pEnt; + if ( !( !friendlyfire.GetBool() && pEntity && pEntity->GetTeamNumber() == GetTeamNumber() ) ) + { + UTIL_ImpactTrace( &tr, iDamageType ); + } + } + } +#endif + + // Get surface where the bullet entered ( if it had different surfaces on enter and exit ) + surfacedata_t *pSurfaceData = physprops->GetSurfaceData( tr.surface.surfaceProps ); + Assert( pSurfaceData ); + + float flMaterialMod = GetDensityFromMaterial(pSurfaceData); + + if ( iDamageType & DMG_MACHINEGUN ) + { + flMaterialMod *= 0.65; + } + + // try to penetrate object + Vector penetrationEnd; + float flMaxDistance = flDamage / flMaterialMod; + +#ifndef CLIENT_DLL + ClearMultiDamage(); + + float flActualDamage = flDamage; + + CTakeDamageInfo dmgInfo( info.m_pAttacker, info.m_pAttacker, flActualDamage, iDamageType ); + CalculateBulletDamageForce( &dmgInfo, info.m_iAmmoType, info.m_vecDirShooting, tr.endpos ); + tr.m_pEnt->DispatchTraceAttack( dmgInfo, info.m_vecDirShooting, &tr ); + + DevMsg( 2, "Giving damage ( %.1f ) to entity of type %s\n", flActualDamage, tr.m_pEnt->GetClassname() ); + + TraceAttackToTriggers( dmgInfo, tr.startpos, tr.endpos, info.m_vecDirShooting ); +#endif + + int stepsize = 16; + + // displacement always stops the bullet + if ( tr.IsDispSurface() ) + { + DevMsg( 2, "bullet was stopped by displacement\n" ); + ApplyMultiDamage(); + break; + } + + // trace through the solid to find the exit point and how much material we went through + if ( !TraceToExit( tr.endpos, info.m_vecDirShooting, penetrationEnd, stepsize, flMaxDistance ) ) + { + DevMsg( 2, "bullet was stopped\n" ); + ApplyMultiDamage(); + break; + } + + // find exact penetration exit + CTraceFilterSimple ignoreShooter( this, iCollisionGroup ); + UTIL_TraceLine( penetrationEnd, tr.endpos, iTraceMask, &ignoreShooter, &reverseTr ); + + // Now we can apply the damage, after we have traced the entity + // so it doesn't break or die before we have a change to test against it +#ifndef CLIENT_DLL + ApplyMultiDamage(); +#endif + + // Continue looking for the exit point + if( reverseTr.m_pEnt != tr.m_pEnt && reverseTr.m_pEnt != NULL ) + { + // something was blocking, trace again + CTraceFilterSkipTwoEntities ignoreShooterAndBlocker( this, reverseTr.m_pEnt, iCollisionGroup ); + UTIL_TraceLine( penetrationEnd, tr.endpos, iTraceMask, &ignoreShooterAndBlocker, &reverseTr ); + } + + if ( sv_showimpacts.GetBool() ) + { + debugoverlay->AddLineOverlay( penetrationEnd, reverseTr.endpos, 255, 0, 0, true, 3.0 ); + } + + // penetration was successful + +#ifdef CLIENT_DLL + // bullet did penetrate object, exit Decal + if ( !( reverseTr.surface.flags & (SURF_SKY|SURF_NODRAW|SURF_HINT|SURF_SKIP) ) ) + { + CBaseEntity *pEntity = reverseTr.m_pEnt; + if ( !( !friendlyfire.GetBool() && pEntity && pEntity->GetTeamNumber() == GetTeamNumber() ) ) + { + UTIL_ImpactTrace( &reverseTr, iDamageType ); + } + } +#endif + + //setup new start end parameters for successive trace + + // New start point is our last exit point + vecSrc = reverseTr.endpos + /* 1.0 * */ info.m_vecDirShooting; + + // Reduce bullet damage by material and distanced travelled through that material + // if it is < 0 we won't go through the loop again + float flTraceDistance = VectorLength( reverseTr.endpos - tr.endpos ); + + flDamage -= flMaterialMod * flTraceDistance; + + if( flDamage > 0 ) + { + DevMsg( 2, "Completed penetration, new damage is %.1f\n", flDamage ); + } + else + { + DevMsg( 2, "bullet was stopped\n" ); + } + + iPenetrations++; + } + +#ifdef GAME_DLL + HandleHeadshotAchievement( iNumHeadshots ); +#endif +} + +void CDODPlayer::SetSprinting( bool bIsSprinting ) +{ + m_Shared.SetSprinting( bIsSprinting ); +} + +bool CDODPlayer::IsSprinting( void ) +{ + float flVelSqr = GetAbsVelocity().LengthSqr(); + + return m_Shared.m_bIsSprinting && ( flVelSqr > 0.5f ); +} + +bool CDODPlayer::CanAttack( void ) +{ + if ( IsSprinting() ) + return false; + + if ( GetMoveType() == MOVETYPE_LADDER ) + return false; + + if ( m_Shared.IsJumping() ) + return false; + + if ( m_Shared.IsDefusing() ) + return false; + + // cannot attack while prone moving. except if you have a bazooka + if ( m_Shared.IsProne() && GetAbsVelocity().LengthSqr() > 1 ) + { + return false; + } + + if( m_Shared.IsGoingProne() || m_Shared.IsGettingUpFromProne() ) + { + return false; + } + + CDODGameRules *rules = DODGameRules(); + + Assert( rules ); + + DODRoundState state = rules->State_Get(); + + if ( dod_bonusround.GetBool() ) + { + if ( GetTeamNumber() == TEAM_ALLIES ) + { + return ( state == STATE_RND_RUNNING || state == STATE_ALLIES_WIN ); + } + else + { + return ( state == STATE_RND_RUNNING || state == STATE_AXIS_WIN ); + } + } + else + return ( state == STATE_RND_RUNNING ); +} + + +void CDODPlayer::SetAnimation( PLAYER_ANIM playerAnim ) +{ + // In DoD, its CPlayerAnimState object manages ALL the animation state. + return; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : const Vector +//----------------------------------------------------------------------------- +const Vector CDODPlayer::GetPlayerMins( void ) const +{ + if ( IsObserver() ) + { + return VEC_OBS_HULL_MIN; + } + else + { + if ( GetFlags() & FL_DUCKING ) + { + return VEC_DUCK_HULL_MIN; + } + else if ( m_Shared.IsProne() ) + { + return VEC_PRONE_HULL_MIN; + } + else + { + return VEC_HULL_MIN; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : const Vector +//----------------------------------------------------------------------------- +const Vector CDODPlayer::GetPlayerMaxs( void ) const +{ + if ( IsObserver() ) + { + return VEC_OBS_HULL_MAX_SCALED( this ); + } + else + { + if ( GetFlags() & FL_DUCKING ) + { + return VEC_DUCK_HULL_MAX_SCALED( this ); + } + else if ( m_Shared.IsProne() ) + { + return VEC_PRONE_HULL_MAX_SCALED( this ); + } + else + { + return VEC_HULL_MAX_SCALED( this ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : collisionGroup - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CDODPlayer::ShouldCollide( int collisionGroup, int contentsMask ) const +{ + if ( collisionGroup == COLLISION_GROUP_PLAYER_MOVEMENT || collisionGroup == COLLISION_GROUP_PROJECTILE ) + { + switch( GetTeamNumber() ) + { + case TEAM_ALLIES: + if ( !( contentsMask & CONTENTS_TEAM2 ) ) + return false; + break; + + case TEAM_AXIS: + if ( !( contentsMask & CONTENTS_TEAM1 ) ) + return false; + break; + } + } + + return BaseClass::ShouldCollide( collisionGroup, contentsMask ); +} + +// --------------------------------------------------------------------------------------------------- // +// CDODPlayerShared implementation. +// --------------------------------------------------------------------------------------------------- // + +CDODPlayerShared::CDODPlayerShared() +{ + m_bProne = false; + m_bForceProneChange = false; + m_flNextProneCheck = 0; + + m_flSlowedUntilTime = 0; + + m_flUnProneTime = 0; + m_flGoProneTime = 0; + + m_flDeployedHeight = STANDING_DEPLOY_HEIGHT; + m_flDeployChangeTime = gpGlobals->curtime; + + SetDesiredPlayerClass( PLAYERCLASS_UNDEFINED ); + + m_flLastViewAnimationTime = gpGlobals->curtime; + + m_pViewOffsetAnim = NULL; +} + +CDODPlayerShared::~CDODPlayerShared() +{ + if ( m_pViewOffsetAnim ) + { + delete m_pViewOffsetAnim; + m_pViewOffsetAnim = NULL; + } +} + +void CDODPlayerShared::Init( CDODPlayer *pPlayer ) +{ + m_pOuter = pPlayer; +} + +bool CDODPlayerShared::IsDucking( void ) const +{ + return !!( m_pOuter->GetFlags() & FL_DUCKING ); +} + +bool CDODPlayerShared::IsProne() const +{ + return m_bProne; +} + +bool CDODPlayerShared::IsGettingUpFromProne() const +{ + return ( m_flUnProneTime > 0 ); +} + +bool CDODPlayerShared::IsGoingProne() const +{ + return ( m_flGoProneTime > 0 ); +} + +void CDODPlayerShared::SetSprinting( bool bSprinting ) +{ + if ( bSprinting && !m_bIsSprinting ) + { + StartSprinting(); + + // only one penalty per key press + if ( m_bGaveSprintPenalty == false ) + { + m_flStamina -= INITIAL_SPRINT_STAMINA_PENALTY; + m_bGaveSprintPenalty = true; + } + } + else if ( !bSprinting && m_bIsSprinting ) + { + StopSprinting(); + } +} + +// this is reset when we let go of the sprint key +void CDODPlayerShared::ResetSprintPenalty( void ) +{ + m_bGaveSprintPenalty = false; +} + +void CDODPlayerShared::StartSprinting( void ) +{ + m_bIsSprinting = true; + +#ifndef CLIENT_DLL + m_pOuter->RemoveHintTimer( HINT_USE_SPRINT ); +#endif +} + +void CDODPlayerShared::StopSprinting( void ) +{ + m_bIsSprinting = false; +} + +void CDODPlayerShared::SetProne( bool bProne, bool bNoAnimation /* = false */ ) +{ + m_bProne = bProne; + + if ( bNoAnimation ) + { + m_flGoProneTime = 0; + m_flUnProneTime = 0; + + // cancel the view animation! + m_bForceProneChange = true; + } + + if ( !bProne /*&& IsSniperZoomed()*/ ) // forceunzoom for going prone is in StartGoingProne + { + ForceUnzoom(); + } +} + +void CDODPlayerShared::SetJumping( bool bJumping ) +{ + m_bJumping = bJumping; + + if ( IsSniperZoomed() ) + { + ForceUnzoom(); + } +} + +void CDODPlayerShared::ForceUnzoom( void ) +{ + CWeaponDODBase *pWeapon = GetActiveDODWeapon(); + if( pWeapon && ( pWeapon->GetDODWpnData().m_WeaponType & WPN_MASK_GUN ) ) + { + CDODSniperWeapon *pSniper = dynamic_cast<CDODSniperWeapon *>(pWeapon); + + if ( pSniper ) + { + pSniper->ZoomOut(); + } + } +} + +bool CDODPlayerShared::IsBazookaDeployed( void ) const +{ + CWeaponDODBase *pWeapon = GetActiveDODWeapon(); + if( pWeapon && pWeapon->GetDODWpnData().m_WeaponType == WPN_TYPE_BAZOOKA ) + { + CDODBaseRocketWeapon *pBazooka = (CDODBaseRocketWeapon *)pWeapon; + return pBazooka->IsDeployed() && !pBazooka->m_bInReload; + } + + return false; +} + +bool CDODPlayerShared::IsBazookaOnlyDeployed( void ) const +{ + CWeaponDODBase *pWeapon = GetActiveDODWeapon(); + if( pWeapon && pWeapon->GetDODWpnData().m_WeaponType == WPN_TYPE_BAZOOKA ) + { + CDODBaseRocketWeapon *pBazooka = (CDODBaseRocketWeapon *)pWeapon; + return pBazooka->IsDeployed(); + } + + return false; +} + +bool CDODPlayerShared::IsSniperZoomed( void ) const +{ + CWeaponDODBase *pWeapon = GetActiveDODWeapon(); + if( pWeapon && ( pWeapon->GetDODWpnData().m_WeaponType & WPN_MASK_GUN ) ) + { + CWeaponDODBaseGun *pGun = (CWeaponDODBaseGun *)pWeapon; + Assert( pGun ); + return pGun->IsSniperZoomed(); + } + + return false; +} + +bool CDODPlayerShared::IsInMGDeploy( void ) const +{ + CWeaponDODBase *pWeapon = GetActiveDODWeapon(); + if( pWeapon && pWeapon->GetDODWpnData().m_WeaponType == WPN_TYPE_MG ) + { + CDODBipodWeapon *pMG = dynamic_cast<CDODBipodWeapon *>( pWeapon ); + Assert( pMG ); + return pMG->IsDeployed(); + } + + return false; +} + +bool CDODPlayerShared::IsProneDeployed( void ) const +{ + return ( IsProne() && IsInMGDeploy() ); +} + +bool CDODPlayerShared::IsSandbagDeployed( void ) const +{ + return ( !IsProne() && IsInMGDeploy() ); +} + +void CDODPlayerShared::SetDesiredPlayerClass( int playerclass ) +{ + m_iDesiredPlayerClass = playerclass; +} + +int CDODPlayerShared::DesiredPlayerClass( void ) +{ + return m_iDesiredPlayerClass; +} + +void CDODPlayerShared::SetPlayerClass( int playerclass ) +{ + m_iPlayerClass = playerclass; +} + +int CDODPlayerShared::PlayerClass( void ) +{ + return m_iPlayerClass; +} + +void CDODPlayerShared::SetStamina( float flStamina ) +{ + m_flStamina = clamp( flStamina, 0, 100 ); +} + +CWeaponDODBase* CDODPlayerShared::GetActiveDODWeapon() const +{ + CBaseCombatWeapon *pWeapon = m_pOuter->GetActiveWeapon(); + if ( pWeapon ) + { + Assert( dynamic_cast< CWeaponDODBase* >( pWeapon ) == static_cast< CWeaponDODBase* >( pWeapon ) ); + return static_cast< CWeaponDODBase* >( pWeapon ); + } + else + { + return NULL; + } +} + +void CDODPlayerShared::SetDeployed( bool bDeployed, float flHeight /* = -1 */ ) +{ + if( gpGlobals->curtime - m_flDeployChangeTime < 0.2 ) + { + Assert(0); + } + + m_flDeployChangeTime = gpGlobals->curtime; + m_vecDeployedAngles = m_pOuter->EyeAngles(); + + if( flHeight > 0 ) + { + m_flDeployedHeight = flHeight; + } + else + { + m_flDeployedHeight = m_pOuter->GetViewOffset()[2]; + } +} + +QAngle CDODPlayerShared::GetDeployedAngles( void ) const +{ + return m_vecDeployedAngles; +} + +void CDODPlayerShared::SetDeployedYawLimits( float flLeftYaw, float flRightYaw ) +{ + m_flDeployedYawLimitLeft = flLeftYaw; + m_flDeployedYawLimitRight = -flRightYaw; + + m_vecDeployedAngles = m_pOuter->EyeAngles(); +} + +void CDODPlayerShared::ClampDeployedAngles( QAngle *vecTestAngles ) +{ + Assert( vecTestAngles ); + + // Clamp Pitch + vecTestAngles->x = clamp( vecTestAngles->x, MAX_DEPLOY_PITCH, MIN_DEPLOY_PITCH ); + + // Clamp Yaw - do a bit more work as yaw will wrap around and cause problems + float flDeployedYawCenter = GetDeployedAngles().y; + + float flDelta = AngleNormalize( vecTestAngles->y - flDeployedYawCenter ); + + if( flDelta < m_flDeployedYawLimitRight ) + { + vecTestAngles->y = flDeployedYawCenter + m_flDeployedYawLimitRight; + } + else if( flDelta > m_flDeployedYawLimitLeft ) + { + vecTestAngles->y = flDeployedYawCenter + m_flDeployedYawLimitLeft; + } + + /* + Msg( "delta %.1f ( left %.1f, right %.1f ) ( %.1f -> %.1f )\n", + flDelta, + flDeployedYawCenter + m_flDeployedYawLimitLeft, + flDeployedYawCenter + m_flDeployedYawLimitRight, + before, + vecTestAngles->y ); + */ + +} + +float CDODPlayerShared::GetDeployedHeight( void ) const +{ + return m_flDeployedHeight; +} + +float CDODPlayerShared::GetSlowedTime( void ) const +{ + return m_flSlowedUntilTime; +} + +void CDODPlayerShared::SetSlowedTime( float t ) +{ + m_flSlowedUntilTime = gpGlobals->curtime + t; +} + +void CDODPlayerShared::StartGoingProne( void ) +{ + // make the prone sound + CPASFilter filter( m_pOuter->GetAbsOrigin() ); + filter.UsePredictionRules(); + m_pOuter->EmitSound( filter, m_pOuter->entindex(), "Player.GoProne" ); + + // slow to prone speed + m_flGoProneTime = gpGlobals->curtime + TIME_TO_PRONE; + + m_flUnProneTime = 0.0f; //reset + + if ( IsSniperZoomed() ) + ForceUnzoom(); +} + +void CDODPlayerShared::StandUpFromProne( void ) +{ + // make the prone sound + CPASFilter filter( m_pOuter->GetAbsOrigin() ); + filter.UsePredictionRules(); + m_pOuter->EmitSound( filter, m_pOuter->entindex(), "Player.UnProne" ); + + // speed up to target speed + m_flUnProneTime = gpGlobals->curtime + TIME_TO_PRONE; + + m_flGoProneTime = 0.0f; //reset +} + +bool CDODPlayerShared::CanChangePosition( void ) +{ + if ( IsInMGDeploy() ) + return false; + + if ( IsGettingUpFromProne() ) + return false; + + if ( IsGoingProne() ) + return false; + + return true; +} + +void CDODPlayer::UpdateStepSound( surfacedata_t *psurface, const Vector &vecOrigin, const Vector &vecVelocity ) +{ + Vector knee; + Vector feet; + float height; + int fLadder; + + if ( m_flStepSoundTime > 0 ) + { + m_flStepSoundTime -= 1000.0f * gpGlobals->frametime; + if ( m_flStepSoundTime < 0 ) + { + m_flStepSoundTime = 0; + } + } + + if ( m_flStepSoundTime > 0 ) + return; + + if ( GetFlags() & (FL_FROZEN|FL_ATCONTROLS)) + return; + + if ( GetMoveType() == MOVETYPE_NOCLIP || GetMoveType() == MOVETYPE_OBSERVER ) + return; + + if ( !sv_footsteps.GetFloat() ) + return; + + float speed = VectorLength( vecVelocity ); + float groundspeed = Vector2DLength( vecVelocity.AsVector2D() ); + + // determine if we are on a ladder + fLadder = ( GetMoveType() == MOVETYPE_LADDER ); + + float flDuck; + + if ( ( GetFlags() & FL_DUCKING) || fLadder ) + { + flDuck = 100; + } + else + { + flDuck = 0; + } + + static float flMinProneSpeed = 10.0f; + static float flMinSpeed = 70.0f; + static float flRunSpeed = 110.0f; + + bool onground = ( GetFlags() & FL_ONGROUND ); + bool movingalongground = ( groundspeed > 0.0f ); + bool moving_fast_enough = ( speed >= flMinSpeed ); + + // always play a step sound if we are moving faster than + + // To hear step sounds you must be either on a ladder or moving along the ground AND + // You must be moving fast enough + + CheckProneMoveSound( groundspeed, onground ); + + if ( !moving_fast_enough || !(fLadder || ( onground && movingalongground )) ) + { + return; + } + + bool bWalking = ( speed < flRunSpeed ); // or ducking! + + VectorCopy( vecOrigin, knee ); + VectorCopy( vecOrigin, feet ); + + height = GetPlayerMaxs()[ 2 ] - GetPlayerMins()[ 2 ]; + + knee[2] = vecOrigin[2] + 0.2 * height; + + float flVol; + + // find out what we're stepping in or on... + if ( fLadder ) + { + psurface = physprops->GetSurfaceData( physprops->GetSurfaceIndex( "ladder" ) ); + flVol = 1.0; + m_flStepSoundTime = 350; + } + else if ( enginetrace->GetPointContents( knee ) & MASK_WATER ) + { + static int iSkipStep = 0; + + if ( iSkipStep == 0 ) + { + iSkipStep++; + return; + } + + if ( iSkipStep++ == 3 ) + { + iSkipStep = 0; + } + psurface = physprops->GetSurfaceData( physprops->GetSurfaceIndex( "wade" ) ); + flVol = 0.65; + m_flStepSoundTime = 600; + } + else if ( enginetrace->GetPointContents( feet ) & MASK_WATER ) + { + psurface = physprops->GetSurfaceData( physprops->GetSurfaceIndex( "water" ) ); + flVol = bWalking ? 0.2 : 0.5; + m_flStepSoundTime = bWalking ? 400 : 300; + } + else + { + if ( !psurface ) + return; + + if ( bWalking ) + { + m_flStepSoundTime = 400; + } + else + { + if ( speed > 200 ) + { + int speeddiff = PLAYER_SPEED_SPRINT - PLAYER_SPEED_RUN; + int diff = speed - PLAYER_SPEED_RUN; + + float percent = (float)diff / (float)speeddiff; + + m_flStepSoundTime = 300.0f - 30.0f * percent; + } + else + { + m_flStepSoundTime = 400; + } + } + + switch ( psurface->game.material ) + { + default: + case CHAR_TEX_CONCRETE: + flVol = bWalking ? 0.2 : 0.5; + break; + + case CHAR_TEX_METAL: + flVol = bWalking ? 0.2 : 0.5; + break; + + case CHAR_TEX_DIRT: + flVol = bWalking ? 0.25 : 0.55; + break; + + case CHAR_TEX_VENT: + flVol = bWalking ? 0.4 : 0.7; + break; + + case CHAR_TEX_GRATE: + flVol = bWalking ? 0.2 : 0.5; + break; + + case CHAR_TEX_TILE: + flVol = bWalking ? 0.2 : 0.5; + break; + + case CHAR_TEX_SLOSH: + flVol = bWalking ? 0.2 : 0.5; + break; + } + } + + m_flStepSoundTime += flDuck; // slower step time if ducking + + if ( GetFlags() & FL_DUCKING ) + { + flVol *= 0.65; + } + + // protect us from prediction errors a little bit + if ( m_flMinNextStepSoundTime > gpGlobals->curtime ) + { + return; + } + + m_flMinNextStepSoundTime = gpGlobals->curtime + 0.1f; + + PlayStepSound( feet, psurface, flVol, false ); +} + +void CDODPlayer::CheckProneMoveSound( int groundspeed, bool onground ) +{ +#ifdef CLIENT_DLL + bool bShouldPlay = (groundspeed > 10) && (onground == true) && m_Shared.IsProne() && IsAlive(); + + if ( m_bPlayingProneMoveSound && !bShouldPlay ) + { + StopSound( "Player.MoveProne" ); + m_bPlayingProneMoveSound= false; + } + else if ( !m_bPlayingProneMoveSound && bShouldPlay ) + { + CRecipientFilter filter; + filter.AddRecipientsByPAS( WorldSpaceCenter() ); + EmitSound( filter, entindex(), "Player.MoveProne" ); + + m_bPlayingProneMoveSound = true; + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : step - +// fvol - +// force - force sound to play +//----------------------------------------------------------------------------- +void CDODPlayer::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force ) +{ + if ( gpGlobals->maxClients > 1 && !sv_footsteps.GetFloat() ) + return; + +#if defined( CLIENT_DLL ) + // during prediction play footstep sounds only once + if ( prediction->InPrediction() && !prediction->IsFirstTimePredicted() ) + return; +#endif + + if ( !psurface ) + return; + + unsigned short stepSoundName = m_Local.m_nStepside ? psurface->sounds.stepleft : psurface->sounds.stepright; + m_Local.m_nStepside = !m_Local.m_nStepside; + + if ( !stepSoundName ) + return; + + IPhysicsSurfaceProps *physprops = MoveHelper( )->GetSurfaceProps(); + const char *pSoundName = physprops->GetString( stepSoundName ); + CSoundParameters params; + + // we don't always know the model, so go by team + char *pModelNameForGender = DOD_PLAYERMODEL_AXIS_RIFLEMAN; + + if( GetTeamNumber() == TEAM_ALLIES ) + pModelNameForGender = DOD_PLAYERMODEL_US_RIFLEMAN; + + if ( !CBaseEntity::GetParametersForSound( pSoundName, params, pModelNameForGender ) ) + return; + + CRecipientFilter filter; + filter.AddRecipientsByPAS( vecOrigin ); + +#ifndef CLIENT_DLL + // im MP, server removed all players in origins PVS, these players + // generate the footsteps clientside + if ( gpGlobals->maxClients > 1 ) + filter.RemoveRecipientsByPVS( vecOrigin ); +#endif + + EmitSound_t ep; + ep.m_nChannel = params.channel; + ep.m_pSoundName = params.soundname; + ep.m_flVolume = fvol; + ep.m_SoundLevel = params.soundlevel; + ep.m_nFlags = 0; + ep.m_nPitch = params.pitch; + ep.m_pOrigin = &vecOrigin; + + EmitSound( filter, entindex(), ep ); +} + +Activity CDODPlayer::TranslateActivity( Activity baseAct, bool *pRequired /* = NULL */ ) +{ + Activity translated = baseAct; + + if ( GetActiveWeapon() ) + { + translated = GetActiveWeapon()->ActivityOverride( baseAct, pRequired ); + } + else if (pRequired) + { + *pRequired = false; + } + + return translated; +} + +void CDODPlayerShared::SetCPIndex( int index ) +{ +#ifdef CLIENT_DLL + + if ( m_pOuter->IsLocalPlayer() ) + { + if ( index == -1 ) + { + // just left an area + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "ObjectiveIconShrink" ); + } + else + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "ObjectiveIconGrow" ); + } + } + +#endif + + m_iCPIndex = index; +} + +void CDODPlayerShared::SetLastViewAnimTime( float flTime ) +{ + m_flLastViewAnimationTime = flTime; +} + +float CDODPlayerShared::GetLastViewAnimTime( void ) +{ + return m_flLastViewAnimationTime; +} + +void CDODPlayerShared::ResetViewOffsetAnimation( void ) +{ + if ( m_pViewOffsetAnim ) + { + //cancel it! + m_pViewOffsetAnim->Reset(); + } +} + +void CDODPlayerShared::ViewOffsetAnimation( Vector vecDest, float flTime, ViewAnimationType type ) +{ + if ( !m_pViewOffsetAnim ) + { + m_pViewOffsetAnim = CViewOffsetAnimation::CreateViewOffsetAnim( m_pOuter ); + } + + Assert( m_pViewOffsetAnim ); + + if ( m_pViewOffsetAnim ) + { + m_pViewOffsetAnim->StartAnimation( m_pOuter->GetViewOffset(), vecDest, flTime, type ); + } +} + +void CDODPlayerShared::ViewAnimThink( void ) +{ + // Check for the flag that will reset our view animations + // when the player respawns + if ( m_bForceProneChange ) + { + ResetViewOffsetAnimation(); + + m_pOuter->SetViewOffset( VEC_VIEW_SCALED( m_pOuter ) ); + + m_bForceProneChange = false; + } + + if ( m_pViewOffsetAnim ) + { + m_pViewOffsetAnim->Think(); + } +} + +void CDODPlayerShared::ComputeWorldSpaceSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs ) +{ + Vector org = m_pOuter->GetAbsOrigin(); + + if ( IsProne() ) + { + static Vector vecProneMin(-44, -44, 0 ); + static Vector vecProneMax(44, 44, 24 ); + + VectorAdd( vecProneMin, org, *pVecWorldMins ); + VectorAdd( vecProneMax, org, *pVecWorldMaxs ); + } + else + { + static Vector vecMin(-32, -32, 0 ); + static Vector vecMax(32, 32, 72 ); + + VectorAdd( vecMin, org, *pVecWorldMins ); + VectorAdd( vecMax, org, *pVecWorldMaxs ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Sets whether this player is dominating the specified other player +//----------------------------------------------------------------------------- +void CDODPlayerShared::SetPlayerDominated( CDODPlayer *pPlayer, bool bDominated ) +{ + int iPlayerIndex = pPlayer->entindex(); + m_bPlayerDominated.Set( iPlayerIndex, bDominated ); + pPlayer->m_Shared.SetPlayerDominatingMe( m_pOuter, bDominated ); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets whether this player is being dominated by the other player +//----------------------------------------------------------------------------- +void CDODPlayerShared::SetPlayerDominatingMe( CDODPlayer *pPlayer, bool bDominated ) +{ + int iPlayerIndex = pPlayer->entindex(); + m_bPlayerDominatingMe.Set( iPlayerIndex, bDominated ); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns whether this player is dominating the specified other player +//----------------------------------------------------------------------------- +bool CDODPlayerShared::IsPlayerDominated( int iPlayerIndex ) +{ +#ifdef CLIENT_DLL + // On the client, we only have data for the local player. + // As a result, it's only valid to ask for dominations related to the local player + C_DODPlayer *pLocalPlayer = C_DODPlayer::GetLocalDODPlayer(); + if ( !pLocalPlayer ) + return false; + + Assert( m_pOuter->IsLocalPlayer() || pLocalPlayer->entindex() == iPlayerIndex ); + + if ( m_pOuter->IsLocalPlayer() ) + return m_bPlayerDominated.Get( iPlayerIndex ); + + return pLocalPlayer->m_Shared.IsPlayerDominatingMe( m_pOuter->entindex() ); +#else + // Server has all the data. + return m_bPlayerDominated.Get( iPlayerIndex ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDODPlayerShared::IsPlayerDominatingMe( int iPlayerIndex ) +{ + return m_bPlayerDominatingMe.Get( iPlayerIndex ); +} diff --git a/game/shared/dod/dod_player_shared.h b/game/shared/dod/dod_player_shared.h new file mode 100644 index 0000000..9d982d3 --- /dev/null +++ b/game/shared/dod/dod_player_shared.h @@ -0,0 +1,285 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef DOD_PLAYER_SHARED_H +#define DOD_PLAYER_SHARED_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "networkvar.h" +#include "weapon_dodbase.h" + + +#ifdef CLIENT_DLL + class C_DODPlayer; +#else + class CDODPlayer; +#endif + +// Entity Messages +#define DOD_PLAYER_POP_HELMET 1 +#define DOD_PLAYER_REMOVE_DECALS 2 + +class CViewOffsetAnimation +{ +public: + CViewOffsetAnimation( CBasePlayer *pPlayer ) + { + m_pPlayer = pPlayer; + m_vecStart = vec3_origin; + m_vecDest = vec3_origin; + m_flLength = 0.0; + m_flEndTime = 0.0; + m_eViewAnimType = VIEW_ANIM_LINEAR_Z_ONLY; + m_bActive = false; + } + + static CViewOffsetAnimation *CreateViewOffsetAnim( CBasePlayer *pPlayer ) + { + CViewOffsetAnimation *p = new CViewOffsetAnimation( pPlayer ); + + Assert( p ); + + return p; + } + + void StartAnimation( Vector vecStart, Vector vecDest, float flTime, ViewAnimationType type ) + { + m_vecStart = vecStart; + m_vecDest = vecDest; + m_flLength = flTime; + m_flEndTime = gpGlobals->curtime + flTime; + m_eViewAnimType = type; + m_bActive = true; + } + + void Reset( void ) + { + m_bActive = false; + } + + void Think( void ) + { + if ( !m_bActive ) + return; + + if ( IsFinished() ) + { + m_bActive = false; + return; + } + + float flFraction = ( m_flEndTime - gpGlobals->curtime ) / m_flLength; + + Assert( m_pPlayer ); + + if ( m_pPlayer ) + { + Vector vecCurrentView = m_pPlayer->GetViewOffset(); + + switch ( m_eViewAnimType ) + { + case VIEW_ANIM_LINEAR_Z_ONLY: + vecCurrentView.z = flFraction * m_vecStart.z + ( 1.0 - flFraction ) * m_vecDest.z; + break; + + case VIEW_ANIM_SPLINE_Z_ONLY: + vecCurrentView.z = SimpleSplineRemapVal( flFraction, 1.0, 0.0, m_vecStart.z, m_vecDest.z ); + break; + + case VIEW_ANIM_EXPONENTIAL_Z_ONLY: + { + float flBias = Bias( flFraction, 0.2 ); + vecCurrentView.z = flBias * m_vecStart.z + ( 1.0 - flBias ) * m_vecDest.z; + } + break; + } + + m_pPlayer->SetViewOffset( vecCurrentView ); + } + } + + bool IsFinished( void ) + { + return ( gpGlobals->curtime > m_flEndTime || m_pPlayer == NULL ); + } + +private: + CBasePlayer *m_pPlayer; + Vector m_vecStart; + Vector m_vecDest; + float m_flEndTime; + float m_flLength; + ViewAnimationType m_eViewAnimType; + bool m_bActive; +}; + + +// Data in the DoD player that is accessed by shared code. +// This data isn't necessarily transmitted between client and server. +class CDODPlayerShared +{ +public: + +#ifdef CLIENT_DLL + friend class C_DODPlayer; + typedef C_DODPlayer OuterClass; + DECLARE_PREDICTABLE(); +#else + friend class CDODPlayer; + typedef CDODPlayer OuterClass; +#endif + + DECLARE_EMBEDDED_NETWORKVAR() + DECLARE_CLASS_NOBASE( CDODPlayerShared ); + + + CDODPlayerShared(); + ~CDODPlayerShared(); + + void SetStamina( float stamina ); + float GetStamina( void ) { return m_flStamina; } + + void Init( OuterClass *pOuter ); + + bool IsProne() const; + bool IsGettingUpFromProne() const; + bool IsGoingProne() const; + void SetProne( bool bProne, bool bNoAnimation = false ); + + bool IsBazookaDeployed( void ) const; + bool IsBazookaOnlyDeployed( void ) const; + bool IsSniperZoomed( void ) const; + bool IsInMGDeploy( void ) const; + bool IsProneDeployed( void ) const; + bool IsSandbagDeployed( void ) const; + bool IsDucking( void ) const; + + void SetDesiredPlayerClass( int playerclass ); + int DesiredPlayerClass( void ); + + void SetPlayerClass( int playerclass ); + int PlayerClass( void ); + + CWeaponDODBase* GetActiveDODWeapon() const; + + void SetDeployed( bool bDeployed, float flHeight = -1 ); + + QAngle GetDeployedAngles( void ) const; + float GetDeployedHeight( void ) const; + + void SetDeployedYawLimits( float flLeftYaw, float flRightYaw ); + void ClampDeployedAngles( QAngle *vecTestAngles ); + + void SetSlowedTime( float t ); + float GetSlowedTime( void ) const; + + void StartGoingProne( void ); + void StandUpFromProne( void ); + + bool CanChangePosition( void ); + + bool IsJumping( void ) { return m_bJumping; } + void SetJumping( bool bJumping ); + + bool IsSprinting( void ) { return m_bIsSprinting; } + + void ForceUnzoom( void ); + + void SetSprinting( bool bSprinting ); + void StartSprinting( void ); + void StopSprinting( void ); + + void SetCPIndex( int index ); + int GetCPIndex( void ) { return m_iCPIndex; } + + void SetLastViewAnimTime( float flTime ); + float GetLastViewAnimTime( void ); + + void ViewAnimThink( void ); + + void ResetViewOffsetAnimation( void ); + void ViewOffsetAnimation( Vector vecDest, float flTime, ViewAnimationType type ); + + void ResetSprintPenalty( void ); + + void SetPlanting( bool bPlanting ) + { + m_bPlanting = bPlanting; + } + + bool IsPlanting( void ) { return m_bPlanting; } + + void SetDefusing( bool bDefusing ) + { + m_bDefusing = bDefusing; + } + + bool IsDefusing( void ) { return m_bDefusing; } + + void ComputeWorldSpaceSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs ); + + void SetPlayerDominated( CDODPlayer *pPlayer, bool bDominated ); + bool IsPlayerDominated( int iPlayerIndex ); + bool IsPlayerDominatingMe( int iPlayerIndex ); + void SetPlayerDominatingMe( CDODPlayer *pPlayer, bool bDominated ); + +private: + + CNetworkVar( bool, m_bProne ); + + CNetworkVar( int, m_iPlayerClass ); + CNetworkVar( int, m_iDesiredPlayerClass ); + + CNetworkVar( float, m_flStamina ); + + CNetworkVar( float, m_flSlowedUntilTime ); + + CNetworkVar( bool, m_bIsSprinting ); + + CNetworkVar( float, m_flDeployedYawLimitLeft ); + CNetworkVar( float, m_flDeployedYawLimitRight ); + + CNetworkVar( bool, m_bPlanting ); + CNetworkVar( bool, m_bDefusing ); + + bool m_bGaveSprintPenalty; + +public: + float m_flNextProneCheck; // Prevent it switching their prone state constantly. + + QAngle m_vecDeployedAngles; + //float m_flDeployedHeight; + CNetworkVar( float, m_flDeployedHeight ); + + CNetworkVar( float, m_flUnProneTime ); + CNetworkVar( float, m_flGoProneTime ); + + CNetworkVar( float, m_flDeployChangeTime ); + + CNetworkVar( bool, m_bForceProneChange ); + + CNetworkVar( int, m_iCPIndex ); + + bool m_bJumping; + + float m_flLastViewAnimationTime; + + CViewOffsetAnimation *m_pViewOffsetAnim; + + CNetworkArray( bool, m_bPlayerDominated, MAX_PLAYERS+1 ); // array of state per other player whether player is dominating other players + CNetworkArray( bool, m_bPlayerDominatingMe, MAX_PLAYERS+1 ); // array of state per other player whether other players are dominating this player + +private: + + OuterClass *m_pOuter; +}; + + + +#endif // DOD_PLAYER_SHARED_H diff --git a/game/shared/dod/dod_playeranimstate.cpp b/game/shared/dod/dod_playeranimstate.cpp new file mode 100644 index 0000000..9570ff9 --- /dev/null +++ b/game/shared/dod/dod_playeranimstate.cpp @@ -0,0 +1,1454 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "dod_playeranimstate.h" +#include "base_playeranimstate.h" +#include "tier0/vprof.h" +#include "animation.h" +#include "studio.h" +#include "apparent_velocity_helper.h" +#include "utldict.h" +#include "weapon_dodbase.h" +#include "dod_shareddefs.h" + +#ifdef CLIENT_DLL + #include "c_dod_player.h" + #include "engine/ivdebugoverlay.h" + #include "filesystem.h" + + ConVar anim_showmainactivity( "anim_showmainactivity", "0", FCVAR_CHEAT, "Show the idle, walk, run, and/or sprint activities." ); +#else + #include "dod_player.h" +#endif + +ConVar anim_showstate( "anim_showstate", "-1", FCVAR_CHEAT | FCVAR_REPLICATED, "Show the (client) animation state for the specified entity (-1 for none)." ); +ConVar anim_showstatelog( "anim_showstatelog", "0", FCVAR_CHEAT | FCVAR_REPLICATED, "1 to output anim_showstate to Msg(). 2 to store in AnimState.log. 3 for both." ); +ConVar dod_bodyheightoffset( "dod_bodyheightoffset", "4", FCVAR_CHEAT | FCVAR_REPLICATED, "Deploy height offset." ); + +#define ANIMPART_STAND "stand" +#define ANIMPART_PRONE "prone" +#define ANIMPART_CROUCH "crouch" +#define ANIMPART_SPRINT "sprint" +#define ANIMPART_SANDBAG "sandbag" +#define ANIMPART_BIPOD "bipod" + +// When moving this fast, he plays run anim. +#define ARBITRARY_RUN_SPEED 300.0f +#define DOD_BODYYAW_RATE 720.0f + +#define DOD_WALK_SPEED 60.0f +#define DOD_RUN_SPEED 120.0f +#define DOD_SPRINT_SPEED 260.0f + +class CDODPlayerAnimState : public CBasePlayerAnimState, public IDODPlayerAnimState +{ +public: + + DECLARE_CLASS( CDODPlayerAnimState, CBasePlayerAnimState ); + friend IDODPlayerAnimState* CreatePlayerAnimState( CDODPlayer *pPlayer ); + + CDODPlayerAnimState(); + + virtual void ShowDebugInfo( void ); + + // This is called by both the client and the server in the same way to trigger events for + // players firing, jumping, throwing grenades, etc. + virtual void DoAnimationEvent( PlayerAnimEvent_t event, int nData ); + virtual void ClearAnimationState(); + virtual Activity CalcMainActivity(); + virtual void Update( float eyeYaw, float eyePitch ); + + virtual void DebugShowAnimState( int iStartLine ); + + virtual int CalcAimLayerSequence( float *flCyle, float *flAimSequenceWeight, bool bForceIdle ) { return 0; } + + virtual float GetCurrentMaxGroundSpeed(); + virtual void ComputeSequences( CStudioHdr *pStudioHdr ); + virtual void ClearAnimationLayers(); + + virtual void RestartMainSequence(); + virtual float CalcMovementPlaybackRate( bool *bIsMoving ); + + Activity TranslateActivity( Activity actDesired ); + void CancelGestures( void ); + +protected: + + // Pose paramters. + bool SetupPoseParameters( CStudioHdr *pStudioHdr ); + virtual void ComputePoseParam_MoveYaw( CStudioHdr *pStudioHdr ); + virtual void ComputePoseParam_AimPitch( CStudioHdr *pStudioHdr ); + virtual void ComputePoseParam_AimYaw( CStudioHdr *pStudioHdr ); + void ComputePoseParam_BodyHeight( CStudioHdr *pStudioHdr ); + virtual void EstimateYaw( void ); + void ConvergeYawAngles( float flGoalYaw, float flYawRate, float flDeltaTime, float &flCurrentYaw ); + + void ComputeFireSequence(); + void ComputeDeployedSequence(); + + void ComputeGestureSequence( CStudioHdr *pStudioHdr ); + + void RestartGesture( int iGestureType, Activity act, bool bAutoKill = true ); + + void UpdateLayerSequenceGeneric( CStudioHdr *pStudioHdr, int iLayer, bool &bEnabled, float &flCurCycle, int &iSequence, bool bWaitAtEnd, float flWeight = 1.0 ); + + void DebugShowAnimStateForPlayer( bool bIsServer ); + void DebugShowEyeYaw( void ); + +// Client specific. +#ifdef CLIENT_DLL + + // Debug. + void DebugShowActivity( Activity activity ); + +#endif + +private: + + void InitDOD( CDODPlayer *pPlayer ); + + bool HandleJumping( Activity *idealActivity ); + bool HandleProne( Activity *idealActivity ); + bool HandleProneDown( CDODPlayer *pPlayer, Activity *idealActivity ); + bool HandleProneUp( CDODPlayer *pPlayer, Activity *idealActivity ); + bool HandleDucked( Activity *idealActivity ); + + bool IsGettingDown( CDODPlayer *pPlayer ); + bool IsGettingUp( CDODPlayer *pPlayer ); + + CDODPlayer* GetOuterDOD() const; + + bool IsPlayingGesture( int type ) + { + return ( m_bPlayingGesture && m_iGestureType == type ); + } + +private: + // Current state variables. + bool m_bJumping; // Set on a jump event. + float m_flJumpStartTime; + bool m_bFirstJumpFrame; + + // These control the prone state _achine. + bool m_bGettingDown; + bool m_bGettingUp; + bool m_bWasGoingProne; + bool m_bWasGettingUp; + + // The single Gesture layer + bool m_bPlayingGesture; + bool m_bAutokillGesture; + int m_iGestureSequence; + float m_flGestureCycle; + + int m_iGestureType; + + enum + { + GESTURE_NONE = -1, + GESTURE_ATTACK1 = 0, + GESTURE_ATTACK2, + GESTURE_RELOAD, + GESTURE_HAND_SIGNAL, + GESTURE_FIDGET, + GESTURE_PLANT, + GESTURE_DEFUSE, + }; + + // Pose parameters. + bool m_bPoseParameterInit; + float m_flEstimateYaw; + float m_flEstimateVelocity; + float m_flLastAimPitch; + float m_flLastAimYaw; + float m_flLastBodyHeight; + float m_flLastAimTurnTime; + Vector2D m_vecLastMoveYaw; + int m_iMoveX; + int m_iMoveY; + int m_iAimYaw; + int m_iAimPitch; + int m_iBodyHeight; + + float m_flFireIdleTime; // Time that we drop our gun + + bool m_bLastDeployState; // true = last was deployed, false = last was not deployed + + DODWeaponID m_iLastWeaponID; // remember the weapon we were last using + + // Our DOD player pointer. + CDODPlayer *m_pOuterDOD; +}; + + +IDODPlayerAnimState* CreatePlayerAnimState( CDODPlayer *pPlayer ) +{ + CDODPlayerAnimState *pState = new CDODPlayerAnimState; + pState->InitDOD( pPlayer ); + return pState; +} + + +// -------------------------------------------------------------------------------- // +// CDODPlayerAnimState implementation. +// -------------------------------------------------------------------------------- // + +CDODPlayerAnimState::CDODPlayerAnimState() +{ + m_bGettingDown = false; + m_bGettingUp = false; + m_bWasGoingProne = false; + m_bWasGettingUp = false; + + m_pOuterDOD = NULL; + + m_bPoseParameterInit = false; + m_flEstimateYaw = 0.0f; + m_flLastAimPitch = 0.0f; + m_flLastAimYaw = 0.0f; + m_flLastBodyHeight = 0.0f; + m_flLastAimTurnTime = 0.0f; + m_vecLastMoveYaw.Init(); + m_iMoveX = -1; + m_iMoveY = -1; + m_iAimYaw = -1; + m_iAimPitch = -1; + m_iBodyHeight = -1; +} + +void CDODPlayerAnimState::InitDOD( CDODPlayer *pPlayer ) +{ + m_pOuterDOD = pPlayer; + + CModAnimConfig config; + config.m_flMaxBodyYawDegrees = 45; + config.m_LegAnimType = LEGANIM_GOLDSRC; + config.m_bUseAimSequences = false; + + BaseClass::Init( pPlayer, config ); +} + + +void CDODPlayerAnimState::ClearAnimationState() +{ + m_bJumping = false; + m_flFireIdleTime = 0; + m_bLastDeployState = false; + m_iLastWeaponID = WEAPON_NONE; + CancelGestures(); + BaseClass::ClearAnimationState(); +} + +void CDODPlayerAnimState::DoAnimationEvent( PlayerAnimEvent_t event, int nData ) +{ + if ( event == PLAYERANIMEVENT_FIRE_GUN ) + { + RestartGesture( GESTURE_ATTACK1, ACT_RANGE_ATTACK1, false ); + + if( GetOuterDOD()->m_Shared.IsBazookaDeployed() ) + { + m_flFireIdleTime = gpGlobals->curtime + 0.1; // don't hold this pose after firing + } + else + { + // hold last frame of fire pose for 2 seconds ( if we are moving ) + m_flFireIdleTime = gpGlobals->curtime + 2; + } + } + if ( event == PLAYERANIMEVENT_SECONDARY_ATTACK ) + { + CancelGestures(); + RestartGesture( GESTURE_ATTACK2, ACT_RANGE_ATTACK2 ); + } + else if ( event == PLAYERANIMEVENT_RELOAD ) + { + CancelGestures(); + RestartGesture( GESTURE_RELOAD, ACT_RELOAD ); + } + else if ( event == PLAYERANIMEVENT_THROW_GRENADE ) + { + CancelGestures(); + RestartGesture( GESTURE_ATTACK1, ACT_RANGE_ATTACK1 ); + } + else if ( event == PLAYERANIMEVENT_ROLL_GRENADE ) + { + CancelGestures(); + RestartGesture( GESTURE_ATTACK2, ACT_RANGE_ATTACK2 ); + } + else if ( event == PLAYERANIMEVENT_JUMP ) + { + // Play the jump animation. + m_bJumping = true; + m_bFirstJumpFrame = true; + RestartMainSequence(); + m_flJumpStartTime = gpGlobals->curtime; + } + else if ( event == PLAYERANIMEVENT_HANDSIGNAL ) + { + CDODPlayer *pPlayer = GetOuterDOD(); + if ( pPlayer && !( pPlayer->m_Shared.IsBazookaDeployed() || pPlayer->m_Shared.IsProne() || pPlayer->m_Shared.IsProneDeployed() || pPlayer->m_Shared.IsSniperZoomed() || pPlayer->m_Shared.IsSandbagDeployed() ) ) + { + CancelGestures(); + RestartGesture( GESTURE_HAND_SIGNAL, ACT_DOD_HS_IDLE ); + } + } + else if ( event == PLAYERANIMEVENT_PLANT_TNT ) + { + CancelGestures(); + RestartGesture( GESTURE_PLANT, ACT_DOD_PLANT_TNT ); + } + else if ( event == PLAYERANIMEVENT_DEFUSE_TNT ) + { + CancelGestures(); + RestartGesture( GESTURE_DEFUSE, ACT_DOD_DEFUSE_TNT ); + } +} + +void CDODPlayerAnimState::ShowDebugInfo( void ) +{ + if ( anim_showstate.GetInt() == m_pOuter->entindex() ) + { + DebugShowAnimStateForPlayer( m_pOuter->IsServer() ); + } +} + + +void CDODPlayerAnimState::RestartMainSequence() +{ + CancelGestures(); + + BaseClass::RestartMainSequence(); +} + +bool CDODPlayerAnimState::HandleJumping( Activity *idealActivity ) +{ + if ( m_bJumping ) + { + if ( m_bFirstJumpFrame ) + { + m_bFirstJumpFrame = false; + RestartMainSequence(); // Reset the animation. + } + + // Don't check if he's on the ground for a sec.. sometimes the client still has the + // on-ground flag set right when the message comes in. + if ( gpGlobals->curtime - m_flJumpStartTime > 0.2f ) + { + if ( m_pOuter->GetFlags() & FL_ONGROUND ) + { + m_bJumping = false; + RestartMainSequence(); + } + } + } + if ( m_bJumping ) + { + *idealActivity = ACT_HOP; + return true; + } + else + { + return false; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Handle the prone up animation. +//----------------------------------------------------------------------------- +bool CDODPlayerAnimState::HandleProneDown( CDODPlayer *pPlayer, Activity *idealActivity ) +{ + if ( ( pPlayer->GetCycle() > 0.99f ) || ( pPlayer->m_Shared.IsProne() ) ) + { + *idealActivity = ACT_PRONE_IDLE; + if ( GetOuterXYSpeed() > MOVING_MINIMUM_SPEED ) + { + *idealActivity = ACT_PRONE_FORWARD; + } + RestartMainSequence(); + + m_bGettingDown = false; + } + else + { + *idealActivity = ACT_GET_DOWN_STAND; + if ( pPlayer->GetFlags() & FL_DUCKING ) + { + *idealActivity = ACT_GET_DOWN_CROUCH; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Handle the prone up animation. +//----------------------------------------------------------------------------- +bool CDODPlayerAnimState::HandleProneUp( CDODPlayer *pPlayer, Activity *idealActivity ) +{ + if ( ( m_pOuter->GetCycle() > 0.99f ) || ( !pPlayer->m_Shared.IsGettingUpFromProne() ) ) + { + m_bGettingUp = false; + RestartMainSequence(); + + return false; + } + + *idealActivity = ACT_GET_UP_STAND; + if ( pPlayer->GetFlags() & FL_DUCKING ) + { + *idealActivity = ACT_GET_UP_CROUCH; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Handle the prone animations. +//----------------------------------------------------------------------------- +bool CDODPlayerAnimState::HandleProne( Activity *idealActivity ) +{ + // Get the player. + CDODPlayer *pPlayer = GetOuterDOD(); + if ( !pPlayer ) + return false; + + // Find the leading edge on going prone. + bool bChange = pPlayer->m_Shared.IsGoingProne() && !m_bWasGoingProne; + m_bWasGoingProne = pPlayer->m_Shared.IsGoingProne(); + if ( bChange ) + { + m_bGettingDown = true; + RestartMainSequence(); + } + + // Find the leading edge on getting up. + bChange = pPlayer->m_Shared.IsGettingUpFromProne() && !m_bWasGettingUp; + m_bWasGettingUp = pPlayer->m_Shared.IsGettingUpFromProne(); + if ( bChange ) + { + m_bGettingUp = true; + RestartMainSequence(); + } + + // Handle the transitions. + if ( m_bGettingDown ) + { + return HandleProneDown( pPlayer, idealActivity ); + } + else if ( m_bGettingUp ) + { + return HandleProneUp( pPlayer, idealActivity ); + } + + // Handle the prone state. + if ( pPlayer->m_Shared.IsProne() ) + { + *idealActivity = ACT_PRONE_IDLE; + if ( GetOuterXYSpeed() > MOVING_MINIMUM_SPEED ) + { + *idealActivity = ACT_PRONE_FORWARD; + } + + return true; + } + + return false; +} + +bool CDODPlayerAnimState::HandleDucked( Activity *idealActivity ) +{ + if ( m_pOuter->GetFlags() & FL_DUCKING ) + { + if ( GetOuterXYSpeed() > MOVING_MINIMUM_SPEED ) + *idealActivity = ACT_RUN_CROUCH; + else + *idealActivity = ACT_CROUCHIDLE; + + return true; + } + else + { + return false; + } +} + +Activity CDODPlayerAnimState::CalcMainActivity() +{ + Activity idealActivity = ACT_IDLE; + + float flSpeed = GetOuterXYSpeed(); + + if ( HandleJumping( &idealActivity ) || + HandleProne( &idealActivity ) || + HandleDucked( &idealActivity ) ) + { + // intentionally blank + } + else + { + if ( flSpeed > MOVING_MINIMUM_SPEED ) + { + if( flSpeed >= DOD_SPRINT_SPEED ) + { + idealActivity = ACT_SPRINT; + + // If we sprint, cancel the fire idle time + CancelGestures(); + } + else if( flSpeed >= DOD_WALK_SPEED ) + idealActivity = ACT_RUN; + else + idealActivity = ACT_WALK; + } + } + + // Shouldn't be here but we need to ship - bazooka deployed reload/running check. + if ( IsPlayingGesture( GESTURE_RELOAD ) ) + { + if ( flSpeed >= DOD_RUN_SPEED && m_pOuterDOD->m_Shared.IsBazookaOnlyDeployed() ) + { + CancelGestures(); + } + } + + ShowDebugInfo(); + + // Client specific. +#ifdef CLIENT_DLL + + if ( anim_showmainactivity.GetBool() ) + { + DebugShowActivity( idealActivity ); + } + +#endif + + return idealActivity; +} + +void CDODPlayerAnimState::CancelGestures( void ) +{ + m_bPlayingGesture = false; + m_iGestureType = GESTURE_NONE; + +#ifdef CLIENT_DLL + m_iGestureSequence = -1; +#else + m_pOuter->RemoveAllGestures(); +#endif +} + +void CDODPlayerAnimState::RestartGesture( int iGestureType, Activity act, bool bAutoKill /* = true */ ) +{ + Activity idealActivity = TranslateActivity( act ); + m_bPlayingGesture = true; + m_iGestureType = iGestureType; + +#ifdef CLIENT_DLL + m_iGestureSequence = m_pOuter->SelectWeightedSequence( idealActivity ); + + if( m_iGestureSequence == -1 ) + { + m_bPlayingGesture = false; + } + + m_flGestureCycle = 0.0f; + m_bAutokillGesture = bAutoKill; +#else + m_pOuterDOD->RestartGesture( idealActivity, true, bAutoKill ); +#endif +} + +Activity CDODPlayerAnimState::TranslateActivity( Activity actDesired ) +{ + Activity idealActivity = actDesired; + + if ( m_pOuterDOD->m_Shared.IsSandbagDeployed() ) + { + switch( idealActivity ) + { + case ACT_IDLE: + idealActivity = ACT_DOD_DEPLOYED; + break; + case ACT_RANGE_ATTACK1: + idealActivity = ACT_DOD_PRIMARYATTACK_DEPLOYED; + break; + case ACT_RELOAD: + idealActivity = ACT_DOD_RELOAD_DEPLOYED; + break; + default: + break; + } + } + else if ( m_pOuterDOD->m_Shared.IsProneDeployed() ) + { + switch( idealActivity ) + { + case ACT_PRONE_IDLE: + idealActivity = ACT_DOD_PRONE_DEPLOYED; + break; + case ACT_RANGE_ATTACK1: + idealActivity = ACT_DOD_PRIMARYATTACK_PRONE_DEPLOYED; + break; + case ACT_RELOAD: + idealActivity = ACT_DOD_RELOAD_PRONE_DEPLOYED; + break; + default: + break; + } + } + else if ( m_pOuterDOD->m_Shared.IsSniperZoomed() || m_pOuterDOD->m_Shared.IsBazookaDeployed() ) + { + switch( idealActivity ) + { + case ACT_IDLE: + idealActivity = ACT_DOD_IDLE_ZOOMED; + break; + case ACT_WALK: + idealActivity = ACT_DOD_WALK_ZOOMED; + break; + case ACT_CROUCHIDLE: + idealActivity = ACT_DOD_CROUCH_ZOOMED; + break; + case ACT_RUN_CROUCH: + idealActivity = ACT_DOD_CROUCHWALK_ZOOMED; + break; + case ACT_PRONE_IDLE: + idealActivity = ACT_DOD_PRONE_ZOOMED; + break; + case ACT_PRONE_FORWARD: + idealActivity = ACT_DOD_PRONE_FORWARD_ZOOMED; + break; + case ACT_RANGE_ATTACK1: + if ( m_pOuterDOD->m_Shared.IsSniperZoomed() ) + { + if( m_pOuterDOD->m_Shared.IsProne() ) + idealActivity = ACT_DOD_PRIMARYATTACK_PRONE; + } + break; + case ACT_RELOAD: + if ( m_pOuterDOD->m_Shared.IsBazookaDeployed() ) + { + if( m_pOuterDOD->m_Shared.IsProne() ) + { + idealActivity = ACT_DOD_RELOAD_PRONE_DEPLOYED; + } + else + { + idealActivity = ACT_DOD_RELOAD_DEPLOYED; + } + } + break; + default: + break; + } + } + else if ( m_pOuterDOD->m_Shared.IsProne() ) + { + // translate prone shooting, reload, handsignal + + switch( idealActivity ) + { + case ACT_RANGE_ATTACK1: + idealActivity = ACT_DOD_PRIMARYATTACK_PRONE; + break; + case ACT_RANGE_ATTACK2: + idealActivity = ACT_DOD_SECONDARYATTACK_PRONE; + break; + case ACT_RELOAD: + idealActivity = ACT_DOD_RELOAD_PRONE; + break; + default: + break; + } + } + else if ( m_pOuter->GetFlags() & FL_DUCKING ) + { + switch( idealActivity ) + { + case ACT_RANGE_ATTACK1: + idealActivity = ACT_DOD_PRIMARYATTACK_CROUCH; + break; + case ACT_RANGE_ATTACK2: + idealActivity = ACT_DOD_SECONDARYATTACK_CROUCH; + break; + case ACT_DOD_HS_IDLE: + idealActivity = ACT_DOD_HS_CROUCH; + break; + } + } + + // Are our guns at fire or rest? + if ( m_flFireIdleTime > gpGlobals->curtime ) + { + switch( idealActivity ) + { + case ACT_IDLE: idealActivity = ACT_DOD_STAND_AIM; break; + case ACT_CROUCHIDLE: idealActivity = ACT_DOD_CROUCH_AIM; break; + case ACT_RUN_CROUCH: idealActivity = ACT_DOD_CROUCHWALK_AIM; break; + case ACT_WALK: idealActivity = ACT_DOD_WALK_AIM; break; + case ACT_RUN: idealActivity = ACT_DOD_RUN_AIM; break; + default: break; + } + } + else + { + switch( idealActivity ) + { + case ACT_IDLE: idealActivity = ACT_DOD_STAND_IDLE; break; + case ACT_CROUCHIDLE: idealActivity = ACT_DOD_CROUCH_IDLE; break; + case ACT_RUN_CROUCH: idealActivity = ACT_DOD_CROUCHWALK_IDLE; break; + case ACT_WALK: idealActivity = ACT_DOD_WALK_IDLE; break; + case ACT_RUN: idealActivity = ACT_DOD_RUN_IDLE; break; + default: break; + } + } + + return m_pOuterDOD->TranslateActivity( idealActivity ); +} + +CDODPlayer* CDODPlayerAnimState::GetOuterDOD() const +{ + return m_pOuterDOD; +} + +float CDODPlayerAnimState::GetCurrentMaxGroundSpeed() +{ + return PLAYER_SPEED_SPRINT; +} + +float CDODPlayerAnimState::CalcMovementPlaybackRate( bool *bIsMoving ) +{ + if( ( GetCurrentMainSequenceActivity() == ACT_GET_UP_STAND ) || ( GetCurrentMainSequenceActivity() == ACT_GET_DOWN_STAND ) || + ( GetCurrentMainSequenceActivity() == ACT_GET_UP_CROUCH ) || ( GetCurrentMainSequenceActivity() == ACT_GET_DOWN_CROUCH ) ) + { + // We don't want to change the playback speed of these, even if we move. + *bIsMoving = false; + return 1.0; + } + + // it would be a good idea to ramp up from 0.5 to 1.0 as they go from stop to moveing, it looks more natural. + *bIsMoving = true; + return 1.0; +} + +void CDODPlayerAnimState::DebugShowAnimState( int iStartLine ) +{ +#ifdef CLIENT_DLL + engine->Con_NPrintf( iStartLine++, "getting down: %s\n", m_bGettingDown ? "yes" : "no" ); + engine->Con_NPrintf( iStartLine++, "getting up: %s\n", m_bGettingUp ? "yes" : "no" ); +#endif + + BaseClass::DebugShowAnimState( iStartLine ); +} + +void CDODPlayerAnimState::ComputeSequences( CStudioHdr *pStudioHdr ) +{ + // Reset some things if we're changed weapons + // do this before ComputeSequences + CWeaponDODBase *pWeapon = GetOuterDOD()->m_Shared.GetActiveDODWeapon(); + if ( pWeapon ) + { + if( pWeapon->GetWeaponID() != m_iLastWeaponID ) + { + CancelGestures(); + m_iLastWeaponID = pWeapon->GetWeaponID(); + m_flFireIdleTime = 0; + } + } + + BaseClass::ComputeSequences( pStudioHdr ); + + if( !m_bGettingDown && !m_bGettingUp ) + { + ComputeFireSequence(); + +#ifdef CLIENT_DLL + + ComputeGestureSequence( pStudioHdr ); + + // get the weapon's swap criteria ( reload? attack? deployed? deployed reload? ) + // and determine whether we should use alt model or not + + CWeaponDODBase *pWeapon = GetOuterDOD()->m_Shared.GetActiveDODWeapon(); + if ( pWeapon ) + { + int iCurrentState = ALTWPN_CRITERIA_NONE; + + if( m_bPlayingGesture && m_iGestureType == GESTURE_ATTACK1 ) + iCurrentState |= ALTWPN_CRITERIA_FIRING; + + else if( m_bPlayingGesture && m_iGestureType == GESTURE_RELOAD ) + iCurrentState |= ALTWPN_CRITERIA_RELOADING; + + if( m_pOuterDOD->m_Shared.IsProne() ) + iCurrentState |= ALTWPN_CRITERIA_PRONE; + + // always use default model while proning or hand signal + if( !IsPlayingGesture( GESTURE_HAND_SIGNAL ) && + !IsPlayingGesture( GESTURE_FIDGET ) && + !m_bGettingDown && + !m_bGettingUp ) + { + pWeapon->CheckForAltWeapon( iCurrentState ); + } + else + { + pWeapon->SetUseAltModel( false ); + } + } +#endif + } +} + +#define GESTURE_LAYER AIMSEQUENCE_LAYER +#define NUM_LAYERS_WANTED (GESTURE_LAYER + 1) + +void CDODPlayerAnimState::ClearAnimationLayers() +{ + if ( !m_pOuter ) + return; + + m_pOuter->SetNumAnimOverlays( NUM_LAYERS_WANTED ); + for ( int i=0; i < m_pOuter->GetNumAnimOverlays(); i++ ) + { + m_pOuter->GetAnimOverlay( i )->SetOrder( CBaseAnimatingOverlay::MAX_OVERLAYS ); + } +} + +void CDODPlayerAnimState::ComputeFireSequence( void ) +{ + // Hold the shoot pose for a time after firing, unless we stand still + if( m_flFireIdleTime < gpGlobals->curtime && + IsPlayingGesture( GESTURE_ATTACK1 ) && + GetOuterXYSpeed() > MOVING_MINIMUM_SPEED ) + { + CancelGestures(); + } + + if( GetOuterDOD()->m_Shared.IsInMGDeploy() != m_bLastDeployState ) + { + CancelGestures(); + + m_bLastDeployState = GetOuterDOD()->m_Shared.IsInMGDeploy(); + } +} + +void CDODPlayerAnimState::ComputeGestureSequence( CStudioHdr *pStudioHdr ) +{ + UpdateLayerSequenceGeneric( pStudioHdr, GESTURE_LAYER, m_bPlayingGesture, m_flGestureCycle, m_iGestureSequence, !m_bAutokillGesture ); +} + +void CDODPlayerAnimState::UpdateLayerSequenceGeneric( CStudioHdr *pStudioHdr, int iLayer, bool &bEnabled, float &flCurCycle, int &iSequence, bool bWaitAtEnd, float flWeight /* = 1.0 */ ) +{ + if ( !bEnabled ) + return; + + if( flCurCycle > 1.0 ) + flCurCycle = 1.0; + + // Increment the fire sequence's cycle. + flCurCycle += m_pOuter->GetSequenceCycleRate( pStudioHdr, iSequence ) * gpGlobals->frametime; + if ( flCurCycle > 1 ) + { + if ( bWaitAtEnd ) + { + flCurCycle = 1; + } + else + { + // Not firing anymore. + bEnabled = false; + iSequence = 0; + return; + } + } + + CAnimationLayer *pLayer = m_pOuter->GetAnimOverlay( iLayer ); + + pLayer->m_flCycle = flCurCycle; + pLayer->m_nSequence = iSequence; + + pLayer->m_flPlaybackRate = 1.0; + pLayer->m_flWeight = flWeight; + pLayer->m_nOrder = iLayer; + +} + +extern ConVar mp_facefronttime; +extern ConVar mp_feetyawrate; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDODPlayerAnimState::Update( float eyeYaw, float eyePitch ) +{ + // Profile the animation update. + VPROF( "CDODPlayerAnimState::Update" ); + + // Get the studio header for the player. + CStudioHdr *pStudioHdr = GetOuterDOD()->GetModelPtr(); + if ( !pStudioHdr ) + return; + + // Check to see if we should be updating the animation state - dead, ragdolled? + if ( !ShouldUpdateAnimState() ) + { + ClearAnimationState(); + return; + } + + // Store the eye angles. + m_flEyeYaw = AngleNormalize( eyeYaw ); + m_flEyePitch = AngleNormalize( eyePitch ); + + // Clear animation overlays because we're about to completely reconstruct them. + ClearAnimationLayers(); + + // Compute the player sequences. + ComputeSequences( pStudioHdr ); + + if ( SetupPoseParameters( pStudioHdr ) ) + { + // Pose parameter - what direction are the player's legs running in. + ComputePoseParam_MoveYaw( pStudioHdr ); + + // Pose parameter - Torso aiming (up/down). + ComputePoseParam_AimPitch( pStudioHdr ); + + // Pose parameter - Torso aiming (rotation). + ComputePoseParam_AimYaw( pStudioHdr ); + + // Pose parameter - Body Height (torso elevation). + ComputePoseParam_BodyHeight( pStudioHdr ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDODPlayerAnimState::SetupPoseParameters( CStudioHdr *pStudioHdr ) +{ + // Check to see if this has already been done. + if ( m_bPoseParameterInit ) + return true; + + // Save off the pose parameter indices. + if ( !pStudioHdr ) + return false; + + // Look for the movement blenders. + m_iMoveX = GetOuterDOD()->LookupPoseParameter( pStudioHdr, "move_x" ); + m_iMoveY = GetOuterDOD()->LookupPoseParameter( pStudioHdr, "move_y" ); + if ( ( m_iMoveX < 0 ) || ( m_iMoveY < 0 ) ) + return false; + + // Look for the aim pitch blender. + m_iAimPitch = GetOuterDOD()->LookupPoseParameter( pStudioHdr, "body_pitch" ); + if ( m_iAimPitch < 0 ) + return false; + + // Look for aim yaw blender. + m_iAimYaw = GetOuterDOD()->LookupPoseParameter( pStudioHdr, "body_yaw" ); + if ( m_iAimYaw < 0 ) + return false; + + // Look for the body height blender. + m_iBodyHeight = GetOuterDOD()->LookupPoseParameter( pStudioHdr, "body_height" ); + if ( m_iBodyHeight < 0 ) + return false; + + m_bPoseParameterInit = true; + + return true; +} + +#define DOD_MOVEMENT_ERROR_LIMIT 1.0 + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDODPlayerAnimState::ComputePoseParam_MoveYaw( CStudioHdr *pStudioHdr ) +{ + // Check to see if we are deployed or prone. + if( GetOuterDOD()->m_Shared.IsInMGDeploy() || GetOuterDOD()->m_Shared.IsProne() ) + { + // Set the 9-way blend movement pose parameters. + Vector2D vecCurrentMoveYaw( 0.0f, 0.0f ); + GetOuter()->SetPoseParameter( pStudioHdr, m_iMoveX, vecCurrentMoveYaw.x ); + GetOuter()->SetPoseParameter( pStudioHdr, m_iMoveY, vecCurrentMoveYaw.y ); + + m_vecLastMoveYaw = vecCurrentMoveYaw; + +#if 0 + // Rotate the entire body instantly. + m_flGoalFeetYaw = AngleNormalize( m_flEyeYaw ); + m_flCurrentFeetYaw = m_flGoalFeetYaw; + m_flLastTurnTime = gpGlobals->curtime; + + // Rotate entire body into position. + m_angRender[YAW] = m_flCurrentFeetYaw; + m_angRender[PITCH] = m_angRender[ROLL] = 0; + + SetOuterBodyYaw( m_flCurrentFeetYaw ); + g_flLastBodyYaw = m_flCurrentFeetYaw; +#endif + } + else + { + // Get the estimated movement yaw. + EstimateYaw(); + + // Get the view yaw. + float flAngle = AngleNormalize( m_flEyeYaw ); + + // rotate movement into local reference frame + float flYaw = flAngle - m_flEstimateYaw; + flYaw = AngleNormalize( -flYaw ); + + // Get the current speed the character is running. + Vector vecEstVelocity; + vecEstVelocity.x = cos( DEG2RAD( flYaw ) ) * m_flEstimateVelocity; + vecEstVelocity.y = sin( DEG2RAD( flYaw ) ) * m_flEstimateVelocity; + + Vector2D vecCurrentMoveYaw( 0.0f, 0.0f ); + // set the pose parameters to the correct direction, but not value + if ( vecEstVelocity.x != 0.0f && fabs( vecEstVelocity.x ) > fabs( vecEstVelocity.y ) ) + { + vecCurrentMoveYaw.x = (vecEstVelocity.x < 0.0) ? -1.0 : 1.0; + vecCurrentMoveYaw.y = vecEstVelocity.y / fabs( vecEstVelocity.x ); + } + else if (vecEstVelocity.y != 0.0f) + { + vecCurrentMoveYaw.y = (vecEstVelocity.y < 0.0) ? -1.0 : 1.0; + vecCurrentMoveYaw.x = vecEstVelocity.x / fabs( vecEstVelocity.y ); + } + +#ifndef CLIENT_DLL + GetOuter()->SetPoseParameter( pStudioHdr, m_iMoveX, vecCurrentMoveYaw.x ); + GetOuter()->SetPoseParameter( pStudioHdr, m_iMoveY, vecCurrentMoveYaw.y ); +#else + + // refine pose parameters to be more accurate + int i = 0; + float dx, dy; + Vector vecAnimVelocity; + + /* + if ( m_pOuter->entindex() == 2 ) + { + GetOuter()->SetPoseParameter( pStudioHdr, m_iMoveX, vecCurrentMoveYaw.x ); + GetOuter()->SetPoseParameter( pStudioHdr, m_iMoveY, vecCurrentMoveYaw.y ); + GetOuterDOD()->GetBlendedLinearVelocity( &vecAnimVelocity ); + DevMsgRT("(%.2f) %.3f : (%.2f) %.3f\n", vecAnimVelocity.x, vecCurrentMoveYaw.x, vecAnimVelocity.y, vecCurrentMoveYaw.y ); + } + */ + + bool retry = true; + do + { + // Set the 9-way blend movement pose parameters. + GetOuter()->SetPoseParameter( pStudioHdr, m_iMoveX, vecCurrentMoveYaw.x ); + GetOuter()->SetPoseParameter( pStudioHdr, m_iMoveY, vecCurrentMoveYaw.y ); + + GetOuterDOD()->GetBlendedLinearVelocity( &vecAnimVelocity ); + + // adjust X pose parameter based on movement error + if (fabs( vecAnimVelocity.x ) > 0.001) + { + vecCurrentMoveYaw.x *= vecEstVelocity.x / vecAnimVelocity.x; + } + else + { + vecCurrentMoveYaw.x = 0; + GetOuter()->SetPoseParameter( pStudioHdr, m_iMoveX, vecCurrentMoveYaw.x ); + } + // adjust Y pose parameter based on movement error + if (fabs( vecAnimVelocity.y ) > 0.001) + { + vecCurrentMoveYaw.y *= vecEstVelocity.y / vecAnimVelocity.y; + } + else + { + vecCurrentMoveYaw.y = 0; + GetOuter()->SetPoseParameter( pStudioHdr, m_iMoveY, vecCurrentMoveYaw.y ); + } + + dx = vecEstVelocity.x - vecAnimVelocity.x; + dy = vecEstVelocity.y - vecAnimVelocity.y; + + retry = (vecCurrentMoveYaw.x < 1.0 && vecCurrentMoveYaw.x > -1.0) && (dx < -DOD_MOVEMENT_ERROR_LIMIT || dx > DOD_MOVEMENT_ERROR_LIMIT); + retry = retry || ((vecCurrentMoveYaw.y < 1.0 && vecCurrentMoveYaw.y > -1.0) && (dy < -DOD_MOVEMENT_ERROR_LIMIT || dy > DOD_MOVEMENT_ERROR_LIMIT)); + + } while (i++ < 5 && retry); + + /* + if ( m_pOuter->entindex() == 2 ) + { + DevMsgRT("%d(%.2f : %.2f) %.3f : (%.2f : %.2f) %.3f\n", + i, + vecEstVelocity.x, vecAnimVelocity.x, vecCurrentMoveYaw.x, + vecEstVelocity.y, vecAnimVelocity.y, vecCurrentMoveYaw.y ); + } + */ +#endif + + m_vecLastMoveYaw = vecCurrentMoveYaw; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDODPlayerAnimState::EstimateYaw( void ) +{ + // Get the frame time. + float flDeltaTime = gpGlobals->frametime; + if ( flDeltaTime == 0.0f ) + { + // FIXME: why does this short circuit? + m_flEstimateVelocity = 0.0; + m_flEstimateYaw = 0.0; + return; + } + + // Get the player's velocity and angles. + Vector vecEstVelocity; + GetOuterAbsVelocity( vecEstVelocity ); + QAngle angles = GetOuterDOD()->GetLocalAngles(); + + // If we are not moving, sync up the feet and eyes slowly. + if ( vecEstVelocity.x == 0.0f && vecEstVelocity.y == 0.0f ) + { + float flYawDelta = angles[YAW] - m_flEstimateYaw; + flYawDelta = AngleNormalize( flYawDelta ); + + if ( flDeltaTime < 0.25f ) + { + flYawDelta *= ( flDeltaTime * 4.0f ); + } + else + { + flYawDelta *= flDeltaTime; + } + + m_flEstimateVelocity = 0.0; + m_flEstimateYaw += flYawDelta; + AngleNormalize( m_flEstimateYaw ); + } + else + { + m_flEstimateVelocity = vecEstVelocity.Length2D(); + m_flEstimateYaw = ( atan2( vecEstVelocity.y, vecEstVelocity.x ) * 180.0f / M_PI ); + m_flEstimateYaw = clamp( m_flEstimateYaw, -180.0f, 180.0f ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDODPlayerAnimState::ComputePoseParam_AimPitch( CStudioHdr *pStudioHdr ) +{ + // Get the view pitch. + float flAimPitch = m_flEyePitch; + + // Lock pitch at 0 if a reload gesture is playing +#ifdef CLIENT_DLL + if ( IsPlayingGesture( GESTURE_RELOAD ) ) + flAimPitch = 0; +#else + Activity idealActivity = TranslateActivity( ACT_RELOAD ); + + if ( m_pOuter->IsPlayingGesture( idealActivity ) ) + flAimPitch = 0; +#endif + + // Set the aim pitch pose parameter and save. + GetOuter()->SetPoseParameter( pStudioHdr, m_iAimPitch, -flAimPitch ); + m_flLastAimPitch = flAimPitch; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDODPlayerAnimState::ComputePoseParam_AimYaw( CStudioHdr *pStudioHdr ) +{ + // Get the movement velocity. + Vector vecVelocity; + GetOuterAbsVelocity( vecVelocity ); + + // Check to see if we are moving. + bool bMoving = ( vecVelocity.Length() > 1.0f ) ? true : false; + + // Check our prone and deployed state. + bool bDeployed = GetOuterDOD()->m_Shared.IsSandbagDeployed() || GetOuterDOD()->m_Shared.IsProneDeployed(); + bool bProne = GetOuterDOD()->m_Shared.IsProne(); + + // If we are moving or are prone and undeployed. + if ( bMoving || ( bProne && !bDeployed ) ) + { + // The feet match the eye direction when moving - the move yaw takes care of the rest. + m_flGoalFeetYaw = m_flEyeYaw; + } + // Else if we are not moving. + else + { + // Initialize the feet. + if ( m_flLastAimTurnTime <= 0.0f ) + { + m_flGoalFeetYaw = m_flEyeYaw; + m_flCurrentFeetYaw = m_flEyeYaw; + m_flLastAimTurnTime = gpGlobals->curtime; + } + // Make sure the feet yaw isn't too far out of sync with the eye yaw. + // TODO: Do something better here! + else + { + float flYawDelta = AngleNormalize( m_flGoalFeetYaw - m_flEyeYaw ); + + if ( bDeployed ) + { + if ( fabs( flYawDelta ) > 20.0f ) + { + float flSide = ( flYawDelta > 0.0f ) ? -1.0f : 1.0f; + m_flGoalFeetYaw += ( 20.0f * flSide ); + } + } + else + { + if ( fabs( flYawDelta ) > m_AnimConfig.m_flMaxBodyYawDegrees ) + { + float flSide = ( flYawDelta > 0.0f ) ? -1.0f : 1.0f; + m_flGoalFeetYaw += ( m_AnimConfig.m_flMaxBodyYawDegrees * flSide ); + } + } + } + } + + // Fix up the feet yaw. + m_flGoalFeetYaw = AngleNormalize( m_flGoalFeetYaw ); + if ( m_flGoalFeetYaw != m_flCurrentFeetYaw ) + { + ConvergeYawAngles( m_flGoalFeetYaw, DOD_BODYYAW_RATE, gpGlobals->frametime, m_flCurrentFeetYaw ); + m_flLastAimTurnTime = gpGlobals->curtime; + } + + // Rotate the body into position. + m_angRender[YAW] = m_flCurrentFeetYaw; + + // Find the aim(torso) yaw base on the eye and feet yaws. + float flAimYaw = m_flEyeYaw - m_flCurrentFeetYaw; + flAimYaw = AngleNormalize( flAimYaw ); + + // Set the aim yaw and save. + GetOuter()->SetPoseParameter( pStudioHdr, m_iAimYaw, -flAimYaw ); + m_flLastAimYaw = flAimYaw; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDODPlayerAnimState::ConvergeYawAngles( float flGoalYaw, float flYawRate, float flDeltaTime, float &flCurrentYaw ) +{ +#define FADE_TURN_DEGREES 60.0f + + // Find the yaw delta. + float flDeltaYaw = flGoalYaw - flCurrentYaw; + float flDeltaYawAbs = fabs( flDeltaYaw ); + flDeltaYaw = AngleNormalize( flDeltaYaw ); + + // Always do at least a bit of the turn (1%). + float flScale = 1.0f; + flScale = flDeltaYawAbs / FADE_TURN_DEGREES; + flScale = clamp( flScale, 0.01f, 1.0f ); + + float flYaw = flYawRate * flDeltaTime * flScale; + if ( flDeltaYawAbs < flYaw ) + { + flCurrentYaw = flGoalYaw; + } + else + { + float flSide = ( flDeltaYaw < 0.0f ) ? -1.0f : 1.0f; + flCurrentYaw += ( flYaw * flSide ); + } + + flCurrentYaw = AngleNormalize( flCurrentYaw ); + +#undef FADE_TURN_DEGREES +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDODPlayerAnimState::ComputePoseParam_BodyHeight( CStudioHdr *pStudioHdr ) +{ + if( m_pOuterDOD->m_Shared.IsSandbagDeployed() ) + { +// float flHeight = m_pOuterDOD->m_Shared.GetDeployedHeight() - 4.0f; + float flHeight = m_pOuterDOD->m_Shared.GetDeployedHeight() - dod_bodyheightoffset.GetFloat(); + GetOuter()->SetPoseParameter( pStudioHdr, m_iBodyHeight, flHeight ); + m_flLastBodyHeight = flHeight; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Anim_StateLog( const char *pMsg, ... ) +{ + // Format the string. + char str[4096]; + va_list marker; + va_start( marker, pMsg ); + Q_vsnprintf( str, sizeof( str ), pMsg, marker ); + va_end( marker ); + + // Log it? + if ( anim_showstatelog.GetInt() == 1 || anim_showstatelog.GetInt() == 3 ) + { + Msg( "%s", str ); + } + + if ( anim_showstatelog.GetInt() > 1 ) + { +// static FileHandle_t hFile = filesystem->Open( "AnimState.log", "wt" ); +// filesystem->FPrintf( hFile, "%s", str ); +// filesystem->Flush( hFile ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Anim_StatePrintf( int iLine, const char *pMsg, ... ) +{ + // Format the string. + char str[4096]; + va_list marker; + va_start( marker, pMsg ); + Q_vsnprintf( str, sizeof( str ), pMsg, marker ); + va_end( marker ); + + // Show it with Con_NPrintf. + engine->Con_NPrintf( iLine, "%s", str ); + + // Log it. + Anim_StateLog( "%s\n", str ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDODPlayerAnimState::DebugShowAnimStateForPlayer( bool bIsServer ) +{ + // Get the player's velocity. + Vector vecVelocity; + GetOuterAbsVelocity( vecVelocity ); + + // Start animation state logging. + int iLine = 5; + if ( bIsServer ) + { + iLine = 12; + } +// Anim_StateLog( "-------------%s: frame %d -----------------\n", bIsServer ? "Server" : "Client", gpGlobals->framecount ); + Anim_StatePrintf( iLine++, "-------------%s: frame %d -----------------\n", bIsServer ? "Server" : "Client", gpGlobals->framecount ); + + // Write out the main sequence and its data. + Anim_StatePrintf( iLine++, "Main: %s, Cycle: %.2f\n", GetSequenceName( GetOuter()->GetModelPtr(), GetOuter()->GetSequence() ), GetOuter()->GetCycle() ); + + // Write out the layers and their data. + for ( int iAnim = 0; iAnim < GetOuter()->GetNumAnimOverlays(); ++iAnim ) + { + CAnimationLayer *pLayer = GetOuter()->GetAnimOverlay( iAnim ); + if ( pLayer && ( pLayer->m_nOrder != CBaseAnimatingOverlay::MAX_OVERLAYS ) ) + { + Anim_StatePrintf( iLine++, "Layer %s: Weight: %.2f, Cycle: %.2f", GetSequenceName( GetOuter()->GetModelPtr(), pLayer->m_nSequence ), (float)pLayer->m_flWeight, (float)pLayer->m_flCycle ); + } + } + + // Write out the speed data. + Anim_StatePrintf( iLine++, "Time: %.2f, Speed: %.2f, MaxSpeed: %.2f", gpGlobals->curtime, vecVelocity.Length2D(), GetCurrentMaxGroundSpeed() ); + + // Write out the 9-way blend data. + Anim_StatePrintf( iLine++, "EntityYaw: %.2f, AimYaw: %.2f, AimPitch: %.2f, MoveX: %.2f, MoveY: %.2f Body: %.2f", m_angRender[YAW], m_flLastAimYaw, m_flLastAimPitch, m_vecLastMoveYaw.x, m_vecLastMoveYaw.y, m_flLastBodyHeight ); + +// Anim_StateLog( "--------------------------------------------\n\n" ); + Anim_StatePrintf( iLine++, "--------------------------------------------\n\n" ); + + DebugShowEyeYaw(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDODPlayerAnimState::DebugShowEyeYaw( void ) +{ +#ifdef _NDEBUG + + float flBaseSize = 10; + float flHeight = 80; + + Vector vecPos = GetOuter()->GetAbsOrigin() + Vector( 0.0f, 0.0f, 3.0f ); + QAngle angles( 0.0f, 0.0f, 0.0f ); + + angles[YAW] = m_flEyeYaw; + + Vector vecForward, vecRight, vecUp; + AngleVectors( angles, &vecForward, &vecRight, &vecUp ); + + // Draw a red triangle on the ground for the eye yaw. + debugoverlay->AddTriangleOverlay( ( vecPos + vecRight * flBaseSize / 2.0f ), + ( vecPos - vecRight * flBaseSize / 2.0f ), + ( vecPos + vecForward * flHeight, 255, 0, 0, 255, false, 0.01f ); + +#endif +} + +// Client specific debug functions. +#ifdef CLIENT_DLL + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDODPlayerAnimState::DebugShowActivity( Activity activity ) +{ +#ifdef _DEBUG + + const char *pszActivity = "other"; + + switch( activity ) + { + case ACT_IDLE: + { + pszActivity = "idle"; + break; + } + case ACT_SPRINT: + { + pszActivity = "sprint"; + break; + } + case ACT_WALK: + { + pszActivity = "walk"; + break; + } + case ACT_RUN: + { + pszActivity = "run"; + break; + } + } + + Msg( "Activity: %s\n", pszActivity ); + +#endif +} + +#endif diff --git a/game/shared/dod/dod_playeranimstate.h b/game/shared/dod/dod_playeranimstate.h new file mode 100644 index 0000000..100b50d --- /dev/null +++ b/game/shared/dod/dod_playeranimstate.h @@ -0,0 +1,62 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef DOD_PLAYERANIMSTATE_H +#define DOD_PLAYERANIMSTATE_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "convar.h" +#include "iplayeranimstate.h" + + +#if defined( CLIENT_DLL ) + class C_DODPlayer; + #define CDODPlayer C_DODPlayer +#else + class CDODPlayer; +#endif + +enum PlayerAnimEvent_t +{ + PLAYERANIMEVENT_FIRE_GUN=0, + PLAYERANIMEVENT_THROW_GRENADE, + PLAYERANIMEVENT_ROLL_GRENADE, + PLAYERANIMEVENT_JUMP, + PLAYERANIMEVENT_RELOAD, + PLAYERANIMEVENT_SECONDARY_ATTACK, + PLAYERANIMEVENT_HANDSIGNAL, + PLAYERANIMEVENT_PLANT_TNT, + PLAYERANIMEVENT_DEFUSE_TNT, + + PLAYERANIMEVENT_HS_NONE, + PLAYERANIMEVENT_CANCEL_GESTURES, // cancel current gesture + + PLAYERANIMEVENT_COUNT +}; + +class IDODPlayerAnimState : virtual public IPlayerAnimState +{ +public: + // This is called by both the client and the server in the same way to trigger events for + // players firing, jumping, throwing grenades, etc. + virtual void DoAnimationEvent( PlayerAnimEvent_t event, int nData = 0 ) = 0; + + virtual void ShowDebugInfo( void ) = 0; +}; + + +IDODPlayerAnimState* CreatePlayerAnimState( CDODPlayer *pPlayer ); + + +// If this is set, then the game code needs to make sure to send player animation events +// to the local player if he's the one being watched. +extern ConVar cl_showanimstate; + + +#endif // DOD_PLAYERANIMSTATE_H diff --git a/game/shared/dod/dod_playerclass_info_parse.cpp b/game/shared/dod/dod_playerclass_info_parse.cpp new file mode 100644 index 0000000..f76d2f1 --- /dev/null +++ b/game/shared/dod/dod_playerclass_info_parse.cpp @@ -0,0 +1,163 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "dod_playerclass_info_parse.h" +#include "dod_shareddefs.h" +#include "weapon_dodbase.h" +#include <KeyValues.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +FilePlayerClassInfo_t* CreatePlayerClassInfo() +{ + return new CDODPlayerClassInfo; +} + +CDODPlayerClassInfo::CDODPlayerClassInfo() +{ + m_iTeam= TEAM_UNASSIGNED; + + m_iPrimaryWeapon= WEAPON_NONE; + m_iSecondaryWeapon= WEAPON_NONE; + m_iMeleeWeapon= WEAPON_NONE; + + m_iNumGrensType1 = 0; + m_iGrenType1 = WEAPON_NONE; + + m_iNumGrensType2 = 0; + m_iGrenType2 = WEAPON_NONE; + + m_iNumBandages = 0; + + m_iHelmetGroup= HELMET_GROUP_0; + m_iHairGroup= HELMET_GROUP_0; + + m_iDropHelmet = HELMET_ALLIES; + + m_szLimitCvar[0] = '\0'; + m_bClassLimitMGMerge = false; +} + +int AliasToWeaponID( const char *alias ) +{ + if (alias) + { + for( int i=0; s_WeaponAliasInfo[i] != NULL; ++i ) + if (!Q_stricmp( s_WeaponAliasInfo[i], alias )) + return i; + } + + return WEAPON_NONE; +} + +void CDODPlayerClassInfo::Parse( KeyValues *pKeyValuesData, const char *szWeaponName ) +{ + BaseClass::Parse( pKeyValuesData, szWeaponName ); + + m_iTeam= pKeyValuesData->GetInt( "team", TEAM_UNASSIGNED ); + + // Figure out what team can have this player class + m_iTeam = TEAM_UNASSIGNED; + const char *pTeam = pKeyValuesData->GetString( "team", NULL ); + if ( pTeam ) + { + if ( Q_stricmp( pTeam, "ALLIES" ) == 0 ) + { + m_iTeam = TEAM_ALLIES; + } + else if ( Q_stricmp( pTeam, "AXIS" ) == 0 ) + { + m_iTeam = TEAM_AXIS; + } + else + { + Assert( false ); + } + } + else + { + Assert( false ); + } + + const char *pszPrimaryWeapon = pKeyValuesData->GetString( "primaryweapon", NULL ); + m_iPrimaryWeapon = AliasToWeaponID( pszPrimaryWeapon ); + Assert( m_iPrimaryWeapon != WEAPON_NONE ); // require player to have a primary weapon + + const char *pszSecondaryWeapon = pKeyValuesData->GetString( "secondaryweapon", NULL ); + + if ( pszSecondaryWeapon ) + { + m_iSecondaryWeapon = AliasToWeaponID( pszSecondaryWeapon ); + Assert( m_iSecondaryWeapon != WEAPON_NONE ); + } + else + m_iSecondaryWeapon = WEAPON_NONE; + + const char *pszMeleeWeapon = pKeyValuesData->GetString( "meleeweapon", NULL ); + if ( pszMeleeWeapon ) + { + m_iMeleeWeapon = AliasToWeaponID( pszMeleeWeapon ); + Assert( m_iMeleeWeapon != WEAPON_NONE ); + } + else + m_iMeleeWeapon = WEAPON_NONE; + + m_iNumGrensType1 = pKeyValuesData->GetInt( "numgrens", 0 ); + if ( m_iNumGrensType1 > 0 ) + { + const char *pszGrenType1 = pKeyValuesData->GetString( "grenadetype", NULL ); + m_iGrenType1 = AliasToWeaponID( pszGrenType1 ); + Assert( m_iGrenType1 != WEAPON_NONE ); + } + + m_iNumGrensType2 = pKeyValuesData->GetInt( "numgrens2", 0 ); + if ( m_iNumGrensType2 > 0 ) + { + const char *pszGrenType2 = pKeyValuesData->GetString( "grenadetype2", NULL ); + m_iGrenType2 = AliasToWeaponID( pszGrenType2 ); + Assert( m_iGrenType2 != WEAPON_NONE ); + } + + m_iNumBandages = pKeyValuesData->GetInt( "numbandages", 0 ); + + m_iHelmetGroup = pKeyValuesData->GetInt( "helmetgroup", 0 ); + m_iHairGroup = pKeyValuesData->GetInt( "hairgroup", 0 ); + + // Which helmet model to generate + const char *pszHelmetModel = pKeyValuesData->GetString( "drophelmet", "HELMET_ALLIES" ); + + if( pszHelmetModel ) + { + if ( Q_stricmp( pszHelmetModel, "HELMET_ALLIES" ) == 0 ) + { + m_iDropHelmet = HELMET_ALLIES; + } + else if ( Q_stricmp( pszHelmetModel, "HELMET_AXIS" ) == 0 ) + { + m_iDropHelmet = HELMET_AXIS; + } + else + { + Assert( false ); + } + } + else + { + Assert( false ); + } + + Q_strncpy( m_szLimitCvar, pKeyValuesData->GetString( "limitcvar", "!! Missing limit cvar on Player Class" ), sizeof(m_szLimitCvar) ); + + Assert( Q_strlen( m_szLimitCvar ) > 0 && "Every class must specify a limitcvar" ); + + m_bClassLimitMGMerge = ( pKeyValuesData->GetInt( "mergemgclass" ) > 0 ); + + // HUD player status health images (when the player is hurt) + Q_strncpy( m_szClassHealthImage, pKeyValuesData->GetString( "healthimage", "white" ), sizeof( m_szClassHealthImage ) ); + Q_strncpy( m_szClassHealthImageBG, pKeyValuesData->GetString( "healthimagebg", "white" ), sizeof( m_szClassHealthImageBG ) ); +}
\ No newline at end of file diff --git a/game/shared/dod/dod_playerclass_info_parse.h b/game/shared/dod/dod_playerclass_info_parse.h new file mode 100644 index 0000000..cb9d37a --- /dev/null +++ b/game/shared/dod/dod_playerclass_info_parse.h @@ -0,0 +1,55 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef DOD_PLAYERCLASS_INFO_PARSE_H +#define DOD_PLAYERCLASS_INFO_PARSE_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "playerclass_info_parse.h" +#include "networkvar.h" +#include "dod_shareddefs.h" + +//-------------------------------------------------------------------------------------------------------- +class CDODPlayerClassInfo : public FilePlayerClassInfo_t +{ +public: + DECLARE_CLASS_GAMEROOT( CDODPlayerClassInfo, FilePlayerClassInfo_t ); + + CDODPlayerClassInfo(); + + virtual void Parse( ::KeyValues *pKeyValuesData, const char *szWeaponName ); + + int m_iTeam; //which team. 2 == allies, 3 == axis + + int m_iPrimaryWeapon; + int m_iSecondaryWeapon; + int m_iMeleeWeapon; + + int m_iNumGrensType1; + int m_iGrenType1; + + int m_iNumGrensType2; + int m_iGrenType2; + + int m_iNumBandages; + + int m_iHelmetGroup; + int m_iHairGroup; //what helmet group to switch to when the helmet comes off + + int m_iDropHelmet; + + char m_szLimitCvar[64]; //which cvar controls the class limit for this class + bool m_bClassLimitMGMerge; // merge class limits with this set to true + + char m_szClassHealthImage[DOD_HUD_HEALTH_IMAGE_LENGTH]; + char m_szClassHealthImageBG[DOD_HUD_HEALTH_IMAGE_LENGTH]; +}; + + +#endif // DOD_PLAYERCLASS_INFO_PARSE_H diff --git a/game/shared/dod/dod_round_timer.cpp b/game/shared/dod/dod_round_timer.cpp new file mode 100644 index 0000000..cf15855 --- /dev/null +++ b/game/shared/dod/dod_round_timer.cpp @@ -0,0 +1,183 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Dod gamerules round timer +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "dod_round_timer.h" + +#ifdef CLIENT_DLL + + #include "iclientmode.h" + #include "vgui_controls/AnimationController.h" + +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#ifdef CLIENT_DLL + + // Use this proxy to flash the round timer whenever the timer is restarted + // because trapping the round start event doesn't work ( the event also flushes + // all hud events and obliterates our TimerFlash event ) + static void RecvProxy_TimerPaused( const CRecvProxyData *pData, void *pStruct, void *pOut ) + { + CDODRoundTimer *pTimer = (CDODRoundTimer *) pStruct; + + bool bTimerPaused = ( pData->m_Value.m_Int > 0 ); + + if ( bTimerPaused == false ) + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "TimerFlash" ); + } + + pTimer->InternalSetPaused( bTimerPaused ); + } + +#endif + +LINK_ENTITY_TO_CLASS( dod_round_timer, CDODRoundTimer ); + +IMPLEMENT_NETWORKCLASS_ALIASED( DODRoundTimer, DT_DODRoundTimer ) + +BEGIN_NETWORK_TABLE_NOBASE( CDODRoundTimer, DT_DODRoundTimer ) + #ifdef CLIENT_DLL + + RecvPropInt( RECVINFO( m_bTimerPaused ), 0, RecvProxy_TimerPaused ), + RecvPropTime( RECVINFO( m_flTimeRemaining ) ), + RecvPropTime( RECVINFO( m_flTimerEndTime ) ), + + #else + + SendPropBool( SENDINFO( m_bTimerPaused ) ), + SendPropTime( SENDINFO( m_flTimeRemaining ) ), + SendPropTime( SENDINFO( m_flTimerEndTime ) ), + + #endif +END_NETWORK_TABLE() + +#ifdef CLIENT_DLL + CDODRoundTimer *g_DODRoundTimer = NULL; +#endif + +//----------------------------------------------------------------------------- +// Purpose: constructor +//----------------------------------------------------------------------------- +CDODRoundTimer::CDODRoundTimer( void ) +{ +#ifndef CLIENT_DLL + m_bTimerPaused = true; + m_flTimeRemaining = 0; + m_iTimerMaxLength = 0; +#else + g_DODRoundTimer = this; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: destructor +//----------------------------------------------------------------------------- +CDODRoundTimer::~CDODRoundTimer( void ) +{ +#ifdef CLIENT_DLL + g_DODRoundTimer = NULL; +#endif +} + +#ifndef CLIENT_DLL + +//----------------------------------------------------------------------------- +// Purpose: The timer is always transmitted to clients +//----------------------------------------------------------------------------- +int CDODRoundTimer::UpdateTransmitState() +{ + // ALWAYS transmit to all clients. + return SetTransmitState( FL_EDICT_ALWAYS ); +} + +#endif + +//----------------------------------------------------------------------------- +// Purpose: To set the initial timer duration +//----------------------------------------------------------------------------- +void CDODRoundTimer::SetTimeRemaining( int iTimerSeconds ) +{ + m_flTimeRemaining = (float)iTimerSeconds; + m_flTimerEndTime = gpGlobals->curtime + m_flTimeRemaining; + m_iTimerMaxLength = iTimerSeconds; +} + +//----------------------------------------------------------------------------- +// Purpose: Timer is paused at round end, stops the countdown +//----------------------------------------------------------------------------- +void CDODRoundTimer::PauseTimer( void ) +{ + if ( m_bTimerPaused == false ) + { + m_bTimerPaused = true; + + m_flTimeRemaining = m_flTimerEndTime - gpGlobals->curtime; + } +} + +//----------------------------------------------------------------------------- +// Purpose: To start or re-start the timer after a pause +//----------------------------------------------------------------------------- +void CDODRoundTimer::ResumeTimer( void ) +{ + if ( m_bTimerPaused == true ) + { + m_bTimerPaused = false; + + m_flTimerEndTime = gpGlobals->curtime + m_flTimeRemaining; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Gets the seconds left on the timer, paused or not. +//----------------------------------------------------------------------------- +float CDODRoundTimer::GetTimeRemaining( void ) +{ + float flSecondsRemaining; + + if ( m_bTimerPaused ) + { + flSecondsRemaining = m_flTimeRemaining; + } + else + { + flSecondsRemaining = m_flTimerEndTime - gpGlobals->curtime; + } + + if ( flSecondsRemaining < 0 ) + flSecondsRemaining = 0; + + return flSecondsRemaining; +} + +//----------------------------------------------------------------------------- +// Purpose: Add seconds to the timer while it is running or paused +//----------------------------------------------------------------------------- +void CDODRoundTimer::AddTimerSeconds( int iSecondsToAdd ) +{ + // do a hud animation indicating that time has been added + + if ( m_bTimerPaused ) + { + m_flTimeRemaining += (float)iSecondsToAdd; + } + else + { + m_flTimerEndTime += (float)iSecondsToAdd; + } + + m_iTimerMaxLength += iSecondsToAdd; +} + +int CDODRoundTimer::GetTimerMaxLength( void ) +{ + return m_iTimerMaxLength; +} diff --git a/game/shared/dod/dod_round_timer.h b/game/shared/dod/dod_round_timer.h new file mode 100644 index 0000000..8d65b41 --- /dev/null +++ b/game/shared/dod/dod_round_timer.h @@ -0,0 +1,67 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Round timer for dod gamerules +// +//=============================================================================// + +#ifndef DOD_ROUND_TIMER_H +#define DOD_ROUND_TIMER_H + +#ifdef _WIN32 +#pragma once +#endif + +#ifdef CLIENT_DLL + #define CDODRoundTimer C_DODRoundTimer +#endif + +class CDODRoundTimer : public CBaseEntity +{ +public: + DECLARE_CLASS( CDODRoundTimer, CBaseEntity ); + DECLARE_NETWORKCLASS(); + + // Constructor + CDODRoundTimer(); + + // Destructor + virtual ~CDODRoundTimer(); + + // Set the initial length of the timer + void SetTimeRemaining( int iTimerSeconds ); + + // Add time to an already running ( or paused ) timer + void AddTimerSeconds( int iSecondsToAdd ); + + void PauseTimer( void ); + void ResumeTimer( void ); + + // Returns seconds to display. + // When paused shows amount of time left once the timer is resumed + float GetTimeRemaining( void ); + + int GetTimerMaxLength( void ); + +#ifndef CLIENT_DLL + + int UpdateTransmitState(); + +#else + + void InternalSetPaused( bool bPaused ) { m_bTimerPaused = bPaused; } + +#endif + +private: + CNetworkVar( bool, m_bTimerPaused ); + CNetworkVar( float, m_flTimeRemaining ); + CNetworkVar( float, m_flTimerEndTime ); + + int m_iTimerMaxLength; // Sum of starting duration plus any time we added +}; + +#ifdef CLIENT_DLL + extern CDODRoundTimer *g_DODRoundTimer; +#endif + +#endif //DOD_ROUND_TIMER_H
\ No newline at end of file diff --git a/game/shared/dod/dod_shareddefs.cpp b/game/shared/dod/dod_shareddefs.cpp new file mode 100644 index 0000000..483fbfa --- /dev/null +++ b/game/shared/dod/dod_shareddefs.cpp @@ -0,0 +1,276 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// +#include "cbase.h" +#include "dod_shareddefs.h" +#include "weapon_dodbase.h" + +//Voice commands +DodVoiceCommand_t g_VoiceCommands[] = +{ + //Voice command // Command Name //HS //Allied subtitle //german subtitle //brit subtitle + + // Menu A + {"voice_attack", "Moveout", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_attack", "#Voice_subtitle_moveout"}, + {"voice_hold", "Hold", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_hold" }, + {"voice_left", "FlankLeft", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_left" }, + {"voice_right", "FlankRight", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_right" }, + {"voice_sticktogether", "StickTogether", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_sticktogether" }, + {"voice_cover", "CoveringFire", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_cover" }, + {"voice_usesmoke", "UseSmoke", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_usesmoke" }, + {"voice_usegrens", "UseGrenades", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_usegrens" }, + {"voice_ceasefire", "CeaseFire", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_ceasefire" }, + + // Menu B + {"voice_yessir", "YesSir", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_yessir" }, + {"voice_negative", "Negative", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_negative" }, + {"voice_backup", "BackUp", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_backup" }, + {"voice_fireinhole", "FireInHole", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_fireinhole" }, + {"voice_grenade", "Grenade", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_grenade" }, + {"voice_sniper", "Sniper", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_sniper" }, + {"voice_niceshot", "NiceShot", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_niceshot" }, + {"voice_thanks", "Thanks", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_thanks" }, + {"voice_areaclear", "Clear", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_areaclear" }, + + // Menu C + {"voice_dropweapons", "DropWeapons", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_dropyourweapons" }, + {"voice_displace", "Displace", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_displace" }, + {"voice_mgahead", "MgAhead", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_mgahead" }, + {"voice_enemybehind", "BehindUs", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_enemybehind" }, + {"voice_wegothim", "WeGotHim", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_wegothim" }, + {"voice_moveupmg", "MoveUpMg", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_moveupmg_30cal", "#Voice_subtitle_moveupmg_mg", "#Voice_subtitle_moveupmg_bren"}, + {"voice_needammo", "NeedAmmo", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_needammo" }, + {"voice_usebazooka", "UseRocket", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_usebazooka", "#Voice_subtitle_usepschreck", "#Voice_subtitle_usepiat"}, + {"voice_bazookaspotted","RocketSpotted", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_pschreckspotted", "#Voice_subtitle_bazookaspotted", "#Voice_subtitle_pschreckspotted"}, + + // Voice commands that aren't in a menu + {"voice_gogogo", "Moveout", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_attack", "#Voice_subtitle_moveout"}, + {"voice_medic", "Medic", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_medic" }, + {"voice_coverflanks", "CoverFlanks", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_coverflanks" }, + {"voice_tank", "TankAhead", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_tigerahead", "#Voice_subtitle_tankahead*" }, + {"voice_takeammo", "TakeAmmo", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_takeammo" }, + {"voice_movewithtank", "MoveWithTank", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_movewithtank" }, + {"voice_wtf", "WhiskeyTangoFoxtrot", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_wtf" }, + {"voice_fireleft", "TakingFireLeft", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_fireleft" }, + {"voice_fireright", "TakingFireRight", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_fireright" }, + {"voice_mgahead", "MgAhead", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_mgahead" }, + {"voice_enemyahead", "EnemyAhead", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_enemyahead" }, + {"voice_fallback", "FallBack", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_fallback" }, + + // Must be last in the list + { NULL } +}; + +// Hand Signals +DodHandSignal_t g_HandSignals[] = +{ + //command // anim event //subtitle + + {"signal_sticktogether", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_sticktogether" }, + {"signal_fallback", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_fallback" }, + {"signal_no", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_negative" }, + {"signal_yes", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_yessir" }, + {"signal_sniper", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_sniper" }, + {"signal_backup", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_backup" }, + {"signal_enemyleft", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_fireleft" }, + {"signal_enemyright", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_fireright" }, + {"signal_grenade", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_grenade" }, + {"signal_flankleft", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_left" }, + {"signal_flankright", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_right" }, + {"signal_moveout", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_attack" }, + {"signal_areaclear", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_areaclear" }, + {"signal_coveringfire", PLAYERANIMEVENT_HANDSIGNAL, "#Voice_subtitle_cover" }, + + // Must be last in the list + { NULL } +}; + +const char * s_WeaponAliasInfo[] = +{ + "none", // WEAPON_NONE = 0, + + //Melee + "amerknife", //WEAPON_AMERKNIFE, + "spade", //WEAPON_SPADE, + + //Pistols + "colt", //WEAPON_COLT, + "p38", //WEAPON_P38, + "c96", //WEAPON_C96 + + //Rifles + "garand", //WEAPON_GARAND, + "m1carbine", //WEAPON_M1CARBINE, + "k98", //WEAPON_K98, + + //Sniper Rifles + "spring", //WEAPON_SPRING, + "k98_scoped", //WEAPON_K98_SCOPED, + + //SMG + "thompson", //WEAPON_THOMPSON, + "mp40", //WEAPON_MP40, + "mp44", //WEAPON_MP44, + "bar", //WEAPON_BAR, + + //Machine guns + "30cal", //WEAPON_30CAL, + "mg42", //WEAPON_MG42, + + //Rocket weapons + "bazooka", //WEAPON_BAZOOKA, + "pschreck", //WEAPON_PSCHRECK, + + //Grenades + "frag_us", //WEAPON_FRAG_US, + "frag_ger", //WEAPON_FRAG_GER, + + "frag_us_live", //WEAPON_FRAG_US_LIVE + "frag_ger_live", //WEAPON_FRAG_GER_LIVE + + "smoke_us", //WEAPON_SMOKE_US + "smoke_ger", //WEAPON_SMOKE_GER + + "riflegren_us", //WEAPON_RIFLEGREN_US + "riflegren_ger", //WEAPON_RIFLEGREN_GER + + "riflegren_us_live", //WEAPON_RIFLEGREN_US_LIVE + "riflegren_ger_live", //WEAPON_RIFLEGREN_GER_LIVE + + // not actually separate weapons, but defines used in stats recording + "thompson_punch", //WEAPON_THOMPSON_PUNCH + "mp40_punch", //WEAPON_MP40_PUNCH + "garand_zoomed", //WEAPON_GARAND_ZOOMED, + + "k98_zoomed", //WEAPON_K98_ZOOMED + "spring_zoomed", //WEAPON_SPRING_ZOOMED + "k98s_zoomed", //WEAPON_K98_SCOPED_ZOOMED + + "30cal_undeployed", //WEAPON_30CAL_UNDEPLOYED, + "mg42_undeployed", //WEAPON_MG42_UNDEPLOYED, + + "bar_semiauto", //WEAPON_BAR_SEMIAUTO, + "mp44_semiauto", //WEAPON_MP44_SEMIAUTO, + + NULL, // end of list marker +}; + +const char *m_pszHelmetModels[NUM_HELMETS] = +{ + "models/helmets/helmet_american.mdl", + "models/helmets/helmet_german.mdl", +}; + +const char *g_pszHintMessages[] = +{ + "#Hint_spotted_a_friend", + "#Hint_spotted_an_enemy", + "#Hint_try_not_to_injure_teammates", + "#Hint_careful_around_teammates", + "#Hint_killing_enemies_is_good", + "#Hint_touched_area_capture", + "#Hint_touched_control_point", + "#Hint_picked_up_object", + "#Hint_mgs_fire_better_deployed", + "#Hint_sandbag_area_touch", + "#Hint_rocket_weapon_pickup", + "#Hint_out_of_ammo", + "#Hint_prone", + "#Hint_low_stamina", + "#Hint_area_requires_object", + "#Hint_player_killed_wavetime", + "#Hint_mg_overheat", + "#game_shoulder_rpg", + "#Hint_pick_up_weapon", + "#Hint_pick_up_grenade", + "#Hint_death_cam", + "#Hint_class_menu", + "#Hint_use_2e_melee", + "#Hint_use_zoom", + "#Hint_use_iron_sights", + "#Hint_use_semi_auto", + "#Hint_use_sprint", + "#Hint_use_deploy", + "#Hint_use_prime", + "#Hint_mg_deploy_usage", + "#Dod_mg_reload", + "#Hint_garand_reload", + "#Hint_turn_off_hints", + "#Hint_need_bomb_to_plant", + "#Hint_bomb_planted", + "#Hint_defuse_bomb", + "#Hint_bomb_target", + "#Hint_bomb_pickup", + "#Hint_bomb_defuse_onground", + "#Hint_bomb_plant_map", + "#Hint_bomb_first_select", +}; + +const char *pszTeamAlliesClasses[] = +{ + "us_garand", + "us_tommy", + "us_bar", + "us_spring", + "us_30cal", + "us_bazooka", + NULL +}; + +const char *pszTeamAxisClasses[] = +{ + "axis_k98", + "axis_mp40", + "axis_mp44", + "axis_k98s", + "axis_mg42", + "axis_pschreck", + NULL +}; + +const char *pszWinPanelCategoryHeaders[] = +{ + "", + "#winpanel_topbomb", + "#winpanel_topcappers", + "#winpanel_topdefenders", + "#winpanel_kills" +}; + +const char *g_pszAchievementAwards[NUM_ACHIEVEMENT_AWARDS] = +{ + "", + "DOD_KILLS_AS_RIFLEMAN", + "DOD_KILLS_AS_ASSAULT", + "DOD_KILLS_AS_SUPPORT", + "DOD_KILLS_AS_SNIPER", + "DOD_KILLS_AS_MG", + "DOD_KILLS_AS_BAZOOKAGUY", + "DOD_ALL_PACK_1", +}; + +const char *g_pszAchievementAwardMaterials_Allies[NUM_ACHIEVEMENT_AWARDS] = +{ + "sprites/player_icons/american", + "sprites/player_icons/american_rifleman", + "sprites/player_icons/american_assault", + "sprites/player_icons/american_support", + "sprites/player_icons/american_sniper", + "sprites/player_icons/american_mg", + "sprites/player_icons/american_rocket", + "sprites/player_icons/american_hero", +}; + +const char *g_pszAchievementAwardMaterials_Axis[NUM_ACHIEVEMENT_AWARDS] = +{ + "sprites/player_icons/german", + "sprites/player_icons/german_rifleman", + "sprites/player_icons/german_assault", + "sprites/player_icons/german_support", + "sprites/player_icons/german_sniper", + "sprites/player_icons/german_mg", + "sprites/player_icons/german_rocket", + "sprites/player_icons/german_hero", +};
\ No newline at end of file diff --git a/game/shared/dod/dod_shareddefs.h b/game/shared/dod/dod_shareddefs.h new file mode 100644 index 0000000..35bae96 --- /dev/null +++ b/game/shared/dod/dod_shareddefs.h @@ -0,0 +1,691 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef DOD_SHAREDDEFS_H +#define DOD_SHAREDDEFS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "dod_playeranimstate.h" + + +#define DOD_PLAYER_VIEW_OFFSET Vector( 0, 0, 54 ) + +// DOD Team IDs. +#define TEAM_ALLIES 2 +#define TEAM_AXIS 3 +#define TEAM_MAXCOUNT 4 // update this if we ever add teams (unlikely) + +enum SubTeam +{ + SUBTEAM_NORMAL = 0, + SUBTEAM_PARA, + SUBTEAM_ALT_NATION, + NUM_SUBTEAMS +}; + +#define MAX_CONTROL_POINTS 8 +#define MAX_CONTROL_POINT_GROUPS 8 + +#define DEATH_CAM_TIME 5.0f + +#define MAX_WAVE_RESPAWN_TIME 20.0f + +#define DOD_BOMB_TIMER_LENGTH 20 +#define DOD_BOMB_DEFUSE_TIME 3.0f +#define DOD_BOMB_PLANT_TIME 2.0f +#define DOD_BOMB_PLANT_RADIUS 80 +#define DOD_BOMB_DEFUSE_MAXDIST 96.0f + +enum +{ + CAP_EVENT_NONE, + CAP_EVENT_BOMB, + CAP_EVENT_FLAG, + CAP_EVENT_TIMER_EXPIRE +}; + +enum +{ + WINPANEL_TOP3_NONE, + WINPANEL_TOP3_BOMBERS, + WINPANEL_TOP3_CAPPERS, + WINPANEL_TOP3_DEFENDERS, + WINPANEL_TOP3_KILLERS +}; + +//-------------- +// DoD Specific damage flags +//-------------- + +// careful when reusing HL2 DMG_ flags, some of them cancel out the damage, eg if they are in DMG_TIMEBASED + +#define DMG_STUN (DMG_PARALYZE) //(1<<15) +#define DMG_MACHINEGUN (DMG_LASTGENERICFLAG<<1) //(1<<30) +#define DMG_BOMB (DMG_LASTGENERICFLAG<<2) //(1<<31) + +//END OF THE EXTENDABLE LIST!! Start reusing HL2 specific flags + +#define MELEE_DMG_SECONDARYATTACK (1<<0) +#define MELEE_DMG_FIST (1<<1) +#define MELEE_DMG_EDGE (1<<2) +#define MELEE_DMG_STRONGATTACK (1<<3) + +#define SANDBAG_NOT_TOUCHING 0 +#define SANDBAG_TOUCHING 1 +#define SANDBAG_TOUCHING_ALIGNED 2 +#define SANDBAG_DEPLOYED 3 + +#define PRONE_DEPLOY_HEIGHT -1 + +#define STANDING_DEPLOY_HEIGHT 58 +#define CROUCHING_DEPLOY_HEIGHT 28 +#define TIME_TO_DEPLOY 0.3 + +#define MIN_DEPLOY_PITCH 45 +#define MAX_DEPLOY_PITCH -60 + +#define VEC_DUCK_MOVING_HULL_MIN Vector(-16, -16, 0 ) +#define VEC_DUCK_MOVING_HULL_MAX Vector( 16, 16, 55 ) + +#define VEC_PRONE_HULL_MIN DODGameRules()->GetDODViewVectors()->m_vProneHullMin +#define VEC_PRONE_HULL_MAX DODGameRules()->GetDODViewVectors()->m_vProneHullMax // MUST be shorter than duck hull for deploy check + +#define VEC_PRONE_HULL_MIN_SCALED( player ) ( DODGameRules()->GetDODViewVectors()->m_vProneHullMin * player->GetModelScale() ) +#define VEC_PRONE_HULL_MAX_SCALED( player ) ( DODGameRules()->GetDODViewVectors()->m_vProneHullMax * player->GetModelScale() ) + +#define VEC_PRONE_HELPER_HULL_MIN Vector(-48, -48, 0 ) +#define VEC_PRONE_HELPER_HULL_MAX Vector( 48, 48, 24 ) + +#define GRENADE_FUSE_LENGTH 5.0 +#define RIFLEGRENADE_FUSE_LENGTH 3.5 + +#define CONTENTS_PRONE_HELPER 0x80000000 + +#define PASS_OUT_CHANGE_TIME 1.5 +#define PASS_OUT_GET_UP_TIME 1.5 + +// DOD-specific viewport panels +#define PANEL_TEAM "team" +#define PANEL_CLASS_ALLIES "class_us" +#define PANEL_CLASS_AXIS "class_ger" +#define PANEL_BACKGROUND "background" + +#define COLOR_DOD_GREEN Color(77, 121, 66, 255) // Color(76, 102, 76, 255) +#define COLOR_DOD_RED Color(255, 64, 64, 255) + +#define DOD_HUD_HEALTH_IMAGE_LENGTH 64 + +// The various states the player can be in during the join game process. +enum DODPlayerState +{ + // Happily running around in the game. + // This state can jump to a bunch of other states like STATE_PICKINGCLASS or STATE_DEATH_ANIM. + STATE_ACTIVE=0, + + // This is the state you're in when you first enter the server. + // It's switching between intro cameras every few seconds, and there's a level info + // screen up. + STATE_WELCOME, // Show the level intro screen. + + // During these states, you can either be a new player waiting to join, or + // you can be a live player in the game who wants to change teams. + // Either way, you can't move while choosing team or class (or while any menu is up). + STATE_PICKINGTEAM, // Choosing team. + STATE_PICKINGCLASS, // Choosing class. + + STATE_DEATH_ANIM, // Playing death anim, waiting for that to finish. + STATE_DEATH_OBSERVING, // Done playing death anim. Waiting for keypress to go into observer mode. + STATE_OBSERVER_MODE, // Noclipping around, watching players, etc. + + NUM_PLAYER_STATES +}; + +enum DODRoundState +{ + // initialize the game, create teams + STATE_INIT=0, + + //Before players have joined the game. Periodically checks to see if enough players are ready + //to start a game. Also reverts to this when there are no active players + STATE_PREGAME, + + //The game is about to start, wait a bit and spawn everyone + STATE_STARTGAME, + + //All players are respawned, frozen in place + STATE_PREROUND, + + //Round is on, playing normally + STATE_RND_RUNNING, + + //Someone has won the round + STATE_ALLIES_WIN, + STATE_AXIS_WIN, + + //Noone has won, manually restart the game, reset scores + STATE_RESTART, + + //Game is over, showing the scoreboard etc + STATE_GAME_OVER, + + NUM_ROUND_STATES +}; + +#define PLAYERCLASS_RANDOM -2 +#define PLAYERCLASS_UNDEFINED -1 + +#define DOD_PLAYERMODEL_AXIS_RIFLEMAN "models/player/german_rifleman.mdl" +#define DOD_PLAYERMODEL_AXIS_ASSAULT "models/player/german_assault.mdl" +#define DOD_PLAYERMODEL_AXIS_SUPPORT "models/player/german_support.mdl" +#define DOD_PLAYERMODEL_AXIS_SNIPER "models/player/german_sniper.mdl" +#define DOD_PLAYERMODEL_AXIS_MG "models/player/german_mg.mdl" +#define DOD_PLAYERMODEL_AXIS_ROCKET "models/player/german_rocket.mdl" + +#define DOD_PLAYERMODEL_US_RIFLEMAN "models/player/american_rifleman.mdl" +#define DOD_PLAYERMODEL_US_ASSAULT "models/player/american_assault.mdl" +#define DOD_PLAYERMODEL_US_SUPPORT "models/player/american_support.mdl" +#define DOD_PLAYERMODEL_US_SNIPER "models/player/american_sniper.mdl" +#define DOD_PLAYERMODEL_US_MG "models/player/american_mg.mdl" +#define DOD_PLAYERMODEL_US_ROCKET "models/player/american_rocket.mdl" + +typedef struct DodClassInfo_s +{ + char selectcmd[32]; + char classname[128]; + char modelname[128]; + + int team; //which team. 0 == allies, 1 == axis + + int primarywpn; + int secondarywpn; + int meleewpn; + + int numgrenades; + int armskin; //what skin does this class show in grenades / knives + + int headgroup; //bodygroups + int helmetgroup; + int geargroup; + int bodygroup; + int hairgroup; //what helmet group to switch to when the helmet comes off + +} DodClassInfo_t; + +extern DodClassInfo_t g_ClassInfo[]; //a structure to hold all of the classes +extern DodClassInfo_t g_ParaClassInfo[]; + +// Voice Commands +typedef struct DodVoiceCommand_s +{ + const char *pszCommandName; // console command that will produce the voice command + + const char *pszSoundName; // name of sound to play + + PlayerAnimEvent_t iHandSignal; //index into the hand signal array + + const char *pszAlliedSubtitle; // subtitles for each nationality + const char *pszAxisSubtitle; + const char *pszBritishSubtitle; + +} DodVoiceCommand_t; + +extern DodVoiceCommand_t g_VoiceCommands[]; + +// Hand Signals +typedef struct DodHandSignal_s +{ + const char *pszCommandName; // console command that will produce the voice command + + PlayerAnimEvent_t iHandSignal; //index into the hand signal array + + const char *pszSubtitle; // subtitles for each nationality + +} DodHandSignal_t; + +extern DodHandSignal_t g_HandSignals[]; + +#define ARM_SKIN_UNDEFINED 0 + +enum +{ + HEAD_GROUP_0 = 0, + HEAD_GROUP_1, + HEAD_GROUP_2, + HEAD_GROUP_3, + HEAD_GROUP_4, + HEAD_GROUP_5, + HEAD_GROUP_6, +}; + +enum +{ + HELMET_GROUP_0 = 0, + HELMET_GROUP_1, + HELMET_GROUP_2, + HELMET_GROUP_3, + HELMET_GROUP_4, + HELMET_GROUP_5, + HELMET_GROUP_6, + HELMET_GROUP_7, +}; + +enum +{ + BODY_GROUP_0 = 0, + BODY_GROUP_1, + BODY_GROUP_2, + BODY_GROUP_3, + BODY_GROUP_4, + BODY_GROUP_5, +}; + +enum +{ + GEAR_GROUP_0 = 0, + GEAR_GROUP_1, + GEAR_GROUP_2, + GEAR_GROUP_3, + GEAR_GROUP_4, + GEAR_GROUP_5, + GEAR_GROUP_6, +}; + +enum +{ + BODYGROUP_BODY = 0, + BODYGROUP_HELMET, + BODYGROUP_HEAD, + BODYGROUP_GEAR, + BODYGROUP_JUMPGEAR +}; + +enum +{ + BODYGROUP_HEAD1 = 0, + BODYGROUP_HEAD2, + BODYGROUP_HEAD3, + BODYGROUP_HEAD4, + BODYGROUP_HEAD5, + BODYGROUP_HEAD6, + BODYGROUP_HEAD7 +}; + +//helmet groups +#define BODYGROUP_HELMET_ON 0 + +#define BODYGROUP_ALLIES_HELMET_HELMET1 0 +#define BODYGROUP_ALLIES_HELMET_HELMET2 1 +#define BODYGROUP_ALLIES_HELMET_OFF 2 + +#define BODYGROUP_AXIS_HAIR0 1 +#define BODYGROUP_AXIS_HAIR1 2 +#define BODYGROUP_AXIS_HAIR2 3 +#define BODYGROUP_AXIS_HAIR3 4 +#define BODYGROUP_AXIS_HAIR4 5 +#define BODYGROUP_AXIS_HAIR5 6 +#define BODYGROUP_AXIS_HAIR6 7 + +//battle gear groups +#define BODYGROUP_TOMMYGEAR 0 +#define BODYGROUP_SPRINGGEAR 1 +#define BODYGROUP_GARANDGEAR 2 +#define BODYGROUP_MGGEAR 3 +#define BODYGROUP_BARGEAR 4 +#define BODYGROUP_CARBGEAR 5 +#define BODYGROUP_GREASEGUNGEAR 6 + +//jump gear +#define BODYGROUP_JUMPGEAR_OFF 0 +#define BODYGROUP_JUMPGEAR_ON 1 + +enum +{ + HELMET_ALLIES = 0, + HELMET_AXIS, + + NUM_HELMETS +}; + +extern const char *m_pszHelmetModels[NUM_HELMETS]; + +//Materials +/* +#define CHAR_TEX_CONCRETE 'C' // texture types +#define CHAR_TEX_METAL 'M' +#define CHAR_TEX_DIRT 'D' +#define CHAR_TEX_GRATE 'G' +#define CHAR_TEX_TILE 'T' +#define CHAR_TEX_WOOD 'W' +#define CHAR_TEX_GLASS 'Y' +#define CHAR_TEX_FLESH 'F' +#define CHAR_TEX_WATER 'S' +#define CHAR_TEX_ROCK 'R' +#define CHAR_TEX_SAND 'A' +#define CHAR_TEX_GRAVEL 'L' +#define CHAR_TEX_STUCCO 'Z' +#define CHAR_TEX_BRICK 'B' +#define CHAR_TEX_SNOW 'N' +#define CHAR_TEX_HEAVYMETAL 'H' +#define CHAR_TEX_LEAVES 'E' +#define CHAR_TEX_SKY 'K' +#define CHAR_TEX_GRASS 'P' +*/ + + +#define WPN_SLOT_PRIMARY 0 +#define WPN_SLOT_SECONDARY 1 +#define WPN_SLOT_MELEE 2 +#define WPN_SLOT_GRENADES 3 +#define WPN_SLOT_BOMB 4 + +#define SLEEVE_AXIS 0 +#define SLEEVE_ALLIES 1 + +#define VM_BODYGROUP_GUN 0 +#define VM_BODYGROUP_SLEEVE 1 + +#define PLAYER_SPEED_FROZEN 1 +#define PLAYER_SPEED_PRONE 50 +#define PLAYER_SPEED_PRONE_ZOOMED 30 +#define PLAYER_SPEED_PRONE_BAZOOKA_DEPLOYED 30 +#define PLAYER_SPEED_ZOOMED 42 +#define PLAYER_SPEED_BAZOOKA_DEPLOYED 50 +#define PLAYER_SPEED_NORMAL 600.0f + +#define PLAYER_SPEED_SLOWED 120 +#define PLAYER_SPEED_RUN 220 +#define PLAYER_SPEED_SPRINT 330 + +#define PUSHAWAY_THINK_INTERVAL (1.0f / 20.0f) + +#define VEC_PRONE_VIEW Vector(0,0,10) +#define VEC_PRONE_VIEW_SCALED( player ) ( Vector(0,0,10) * player->GetModelScale() ) + +#define TIME_TO_PRONE 1.2f // should be 1.5! + +#define INITIAL_SPRINT_STAMINA_PENALTY 15 +#define LOW_STAMINA_THRESHOLD 35 + +// changed to 80% of goldsrc values, gives the same end result +#define ZOOM_SWAY_PRONE 0.1 +#define ZOOM_SWAY_DUCKING 0.2 +#define ZOOM_SWAY_STANDING 0.5 +#define ZOOM_SWAY_MOVING_PENALTY 0.4 + +extern const char * s_WeaponAliasInfo[]; + +enum +{ + //Dod hint messages + HINT_FRIEND_SEEN = 0, // #Hint_spotted_a_friend + HINT_ENEMY_SEEN, // #Hint_spotted_an_enemy + HINT_FRIEND_INJURED, // #Hint_try_not_to_injure_teammates + HINT_FRIEND_KILLED, // #Hint_careful_around_teammates + HINT_ENEMY_KILLED, // #Hint_killing_enemies_is_good + HINT_IN_AREA_CAP, // #Hint_touched_area_capture + HINT_FLAG_TOUCH, // #Hint_touched_control_point + HINT_OBJECT_PICKUP, // #Hint_picked_up_object + HINT_MG_FIRE_UNDEPLOYED, // #Hint_mgs_fire_better_deployed + HINT_SANDBAG_AREA, // #Hint_sandbag_area_touch + HINT_BAZOOKA_PICKUP, // #Hint_rocket_weapon_pickup + HINT_AMMO_EXHAUSTED, // #Hint_out_of_ammo + HINT_PRONE, // #Hint_prone + HINT_LOW_STAMINA, // #Hint_low_stamina + HINT_OBJECT_REQUIRED, // #Hint_area_requires_object + HINT_PLAYER_KILLED_WAVETIME, // #Hint_player_killed_wavetime + HINT_WEAPON_OVERHEAT, // #Hint_mg_overheat + HINT_SHOULDER_WEAPON, // #game_shoulder_rpg + + HINT_PICK_UP_WEAPON, // #Hint_pick_up_weapon + HINT_PICK_UP_GRENADE, // #Hint_pick_up_grenade + HINT_DEATHCAM, // #Hint_death_cam + HINT_CLASSMENU, // #Hint_class_menu + + HINT_USE_MELEE, // #Hint_use_2e_melee + HINT_USE_ZOOM, // #Hint_use_zoom + HINT_USE_IRON_SIGHTS, // #Hint_use_iron_sights + HINT_USE_SEMI_AUTO, // #Hint_use_semi_auto + HINT_USE_SPRINT, // #Hint_use_sprint + HINT_USE_DEPLOY, // #Hint_use_deploy + HINT_USE_PRIME, // #Hint_use_prime + + HINT_MG_DEPLOY_USAGE, // #Hint_mg_deploy_usage + + HINT_MG_DEPLOY_TO_RELOAD, // #Dod_mg_reload + HINT_GARAND_RELOAD, // #Hint_garand_reload + + HINT_TURN_OFF_HINTS, // #Hint_turn_off_hints + + HINT_NEED_BOMB, // #Hint_need_bomb_to_plant + HINT_BOMB_PLANTED, // #Hint_bomb_planted + HINT_DEFUSE_BOMB, // #Hint_defuse_bomb + HINT_BOMB_TARGET, // #Hint_bomb_target + HINT_BOMB_PICKUP, // #Hint_bomb_pickup + HINT_BOMB_DEFUSE_ONGROUND, // #Hint_bomb_defuse_onground + + HINT_BOMB_PLANT_MAP, // #Hint_bomb_plant_map + HINT_BOMB_FIRST_SELECT, // #Hint_bomb_first_select + + NUM_HINTS +}; + +extern const char *g_pszHintMessages[]; + +// HINT_xxx bits to clear when the round restarts +#define HINT_MASK_SPAWN_CLEAR ( 1 << HINT_FRIEND_KILLED ) + +// criteria for when a weapon model wants to use an alt model +#define ALTWPN_CRITERIA_NONE 0 +#define ALTWPN_CRITERIA_FIRING (1 << 0) +#define ALTWPN_CRITERIA_RELOADING (1 << 1) +#define ALTWPN_CRITERIA_DEPLOYED (1 << 2) +#define ALTWPN_CRITERIA_DEPLOYED_RELOAD (1 << 3) +#define ALTWPN_CRITERIA_PRONE (1 << 4) // player is prone +#define ALTWPN_CRITERIA_PRONE_DEPLOYED_RELOAD (1 << 5) // player should use special alt model when prone deployed reloading + +// eject brass shells +#define EJECTBRASS_PISTOL 0 +#define EJECTBRASS_RIFLE 1 +#define EJECTBRASS_MG 2 +#define EJECTBRASS_MG_2 3 // ? +#define EJECTBRASS_GARANDCLIP 4 + +extern const char *pszTeamAlliesClasses[]; +extern const char *pszTeamAxisClasses[]; + +enum +{ + DOD_COLLISIONGROUP_SHELLS = LAST_SHARED_COLLISION_GROUP, + DOD_COLLISIONGROUP_BLOCKERWALL, +}; + +enum +{ + PROGRESS_BAR_BANDAGER = 0, + PROGRESS_BAR_BANDAGEE, + PROGRESS_BAR_CAP, // done by objective resource + + NUM_PROGRESS_BAR_TYPES +}; + +// used for the corner cut panels in the HUD +enum +{ + DOD_CORNERCUT_PANEL_BOTTOMRIGHT = 0, + DOD_CORNERCUT_PANEL_BOTTOMLEFT, + DOD_CORNERCUT_PANEL_TOPRIGHT, + DOD_CORNERCUT_PANEL_TOPLEFT, +}; + +enum ViewAnimationType { + VIEW_ANIM_LINEAR_Z_ONLY, + VIEW_ANIM_SPLINE_Z_ONLY, + VIEW_ANIM_EXPONENTIAL_Z_ONLY, +}; + +enum BombTargetState +{ + // invisible, not active + BOMB_TARGET_INACTIVE=0, + + // visible, accepts planting +use + BOMB_TARGET_ACTIVE, + + // visible, accepts disarm +use, counts down to explosion + // if disarmed, returns to BOMB_TARGET_ACTIVE + // if explodes, returns to BOMB_TARGET_INACTIVE + BOMB_TARGET_ARMED, + + NUM_BOMB_TARGET_STATES +}; + +extern const char *pszWinPanelCategoryHeaders[]; + +enum +{ + // Season 1 + ACHIEVEMENT_DOD_THROW_BACK_GREN = 0, + ACHIEVEMENT_DOD_CONSECUTIVE_HEADSHOTS, + ACHIEVEMENT_DOD_MG_POSITION_STREAK, + ACHIEVEMENT_DOD_WIN_KNIFE_FIGHT, + ACHIEVEMENT_DOD_PLAY_CUSTOM_MAPS, + ACHIEVEMENT_DOD_KILLS_WITH_GRENADE, + ACHIEVEMENT_DOD_LONG_RANGE_ROCKET, + ACHIEVEMENT_DOD_END_ROUND_KILLS, + ACHIEVEMENT_DOD_CAP_LAST_FLAG, + ACHIEVEMENT_DOD_USE_ENEMY_WEAPONS, + ACHIEVEMENT_DOD_KILL_DOMINATING_MG, + ACHIEVEMENT_DOD_COLMAR_DEFENSE, + ACHIEVEMENT_DOD_BLOCK_CAPTURES, + ACHIEVEMENT_DOD_JAGD_OVERTIME_CAP, + ACHIEVEMENT_DOD_WEAPON_MASTERY, + + // grinds + ACHIEVEMENT_DOD_KILLS_AS_ALLIES, + ACHIEVEMENT_DOD_KILLS_AS_AXIS, + + ACHIEVEMENT_DOD_KILLS_AS_RIFLEMAN, + ACHIEVEMENT_DOD_KILLS_AS_ASSAULT, + ACHIEVEMENT_DOD_KILLS_AS_SUPPORT, + ACHIEVEMENT_DOD_KILLS_AS_SNIPER, + ACHIEVEMENT_DOD_KILLS_AS_MG, + ACHIEVEMENT_DOD_KILLS_AS_BAZOOKAGUY, + + ACHIEVEMENT_DOD_KILLS_WITH_GARAND, + ACHIEVEMENT_DOD_KILLS_WITH_THOMPSON, + ACHIEVEMENT_DOD_KILLS_WITH_BAR, + ACHIEVEMENT_DOD_KILLS_WITH_SPRING, + ACHIEVEMENT_DOD_KILLS_WITH_30CAL, + ACHIEVEMENT_DOD_KILLS_WITH_BAZOOKA, + ACHIEVEMENT_DOD_KILLS_WITH_K98, + ACHIEVEMENT_DOD_KILLS_WITH_MP40, + ACHIEVEMENT_DOD_KILLS_WITH_MP44, + ACHIEVEMENT_DOD_KILLS_WITH_K98SCOPED, + ACHIEVEMENT_DOD_KILLS_WITH_MG42, + ACHIEVEMENT_DOD_KILLS_WITH_PSCHRECK, + ACHIEVEMENT_DOD_KILLS_WITH_COLT, + ACHIEVEMENT_DOD_KILLS_WITH_P38, + ACHIEVEMENT_DOD_KILLS_WITH_C96, + ACHIEVEMENT_DOD_KILLS_WITH_M1CARBINE, + ACHIEVEMENT_DOD_KILLS_WITH_AMERKNIFE, + ACHIEVEMENT_DOD_KILLS_WITH_SPADE, + ACHIEVEMENT_DOD_KILLS_WITH_PUNCH, + ACHIEVEMENT_DOD_KILLS_WITH_FRAG_US, + ACHIEVEMENT_DOD_KILLS_WITH_FRAG_GER, + ACHIEVEMENT_DOD_KILLS_WITH_RIFLEGREN_US, + ACHIEVEMENT_DOD_KILLS_WITH_RIFLEGREN_GER, + + ACHIEVEMENT_DOD_CAPTURE_GRIND, + ACHIEVEMENT_DOD_BLOCK_CAPTURES_GRIND, + ACHIEVEMENT_DOD_ROUNDS_WON_GRIND, + + ACHIEVEMENT_DOD_BOMBS_PLANTED_GRIND, + ACHIEVEMENT_DOD_BOMBS_DEFUSED_GRIND, + + ACHIEVEMENT_DOD_ALL_PACK_1, + + ACHIEVEMENT_DOD_BEAT_THE_HEAT, + + // Winter 2011 + ACHIEVEMENT_DOD_COLLECT_HOLIDAY_GIFTS, + + NUM_DOD_ACHIEVEMENTS +}; + +#define ACHIEVEMENT_NUM_CONSECUTIVE_HEADSHOTS 5 +#define ACHIEVEMENT_MG_STREAK_IS_DOMINATING 8 +#define ACHIEVEMENT_NUM_ENEMY_WPN_KILLS 5 +#define ACHIEVEMENT_LONG_RANGE_ROCKET_DIST 1200 +// other magic numbers exist inside the achievements themselves in achievements_dod.cpp + +enum +{ + DOD_MUZZLEFLASH_PISTOL = 0, + DOD_MUZZLEFLASH_AUTO, + DOD_MUZZLEFLASH_RIFLE, + DOD_MUZZLEFLASH_MG, + DOD_MUZZLEFLASH_ROCKET, + DOD_MUZZLEFLASH_MG42 +}; + +#define DOD_KILLS_DOMINATION 4 + +// Death notice flags +#define DOD_DEATHFLAG_DOMINATION 0x0001 // killer is dominating victim +#define DOD_DEATHFLAG_REVENGE 0x0002 // killer got revenge on victim + +enum +{ + ACHIEVEMENT_AWARDS_NONE = 0, + ACHIEVEMENT_AWARDS_RIFLEMAN, + ACHIEVEMENT_AWARDS_ASSAULT, + ACHIEVEMENT_AWARDS_SUPPORT, + ACHIEVEMENT_AWARDS_SNIPER, + ACHIEVEMENT_AWARDS_MG, + ACHIEVEMENT_AWARDS_ROCKET, + ACHIEVEMENT_AWARDS_ALL_PACK_1, + + NUM_ACHIEVEMENT_AWARDS +}; + +extern const char *g_pszAchievementAwards[NUM_ACHIEVEMENT_AWARDS]; +extern const char *g_pszAchievementAwardMaterials_Allies[NUM_ACHIEVEMENT_AWARDS]; +extern const char *g_pszAchievementAwardMaterials_Axis[NUM_ACHIEVEMENT_AWARDS]; + +enum DODStatType_t +{ + DODSTAT_PLAYTIME = 0, + DODSTAT_ROUNDSWON, + DODSTAT_ROUNDSLOST, + DODSTAT_KILLS, + DODSTAT_DEATHS, + DODSTAT_CAPTURES, + DODSTAT_BLOCKS, + DODSTAT_BOMBSPLANTED, + DODSTAT_BOMBSDEFUSED, + DODSTAT_DOMINATIONS, + DODSTAT_REVENGES, + DODSTAT_SHOTS_HIT, + DODSTAT_SHOTS_FIRED, + DODSTAT_HEADSHOTS, + + DODSTAT_MAX +}; + +#define DODSTAT_FIRST DODSTAT_PLAYTIME + +typedef struct +{ + int m_iStat[DODSTAT_MAX]; + bool m_bDirty[DODSTAT_MAX]; + +} dod_stat_accumulator_t; + +#define NUM_DOD_PLAYERCLASSES 6 + +#endif // DOD_SHAREDDEFS_H diff --git a/game/shared/dod/dod_usermessages.cpp b/game/shared/dod/dod_usermessages.cpp new file mode 100644 index 0000000..ad704aa --- /dev/null +++ b/game/shared/dod/dod_usermessages.cpp @@ -0,0 +1,65 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "usermessages.h" +#include "shake.h" +#include "voice_gamemgr.h" + +// NVNT include to register in haptic user messages +#include "haptics/haptic_msgs.h" + +void RegisterUserMessages() +{ + usermessages->Register( "Geiger", 1 ); // geiger info data + usermessages->Register( "Train", 1 ); // train control data + usermessages->Register( "HudText", -1 ); + usermessages->Register( "SayText", -1 ); + usermessages->Register( "TextMsg", -1 ); + usermessages->Register( "ResetHUD", 1 ); // called every respawn + usermessages->Register( "GameTitle", 0 ); // show game title + usermessages->Register( "ItemPickup", -1 ); // for item history on screen + usermessages->Register( "ShowMenu", -1 ); // show hud menu + usermessages->Register( "Shake", 13 ); // shake view + usermessages->Register( "Fade", 10 ); // fade HUD in/out + usermessages->Register( "VGUIMenu", -1 ); // Show VGUI menu + usermessages->Register( "Rumble", 3 ); // Send a rumble to a controller + usermessages->Register( "CloseCaption", -1 ); // Show a caption (by string id number)(duration in 10th of a second) + + usermessages->Register( "VoiceMask", VOICE_MAX_PLAYERS_DW*4 * 2 + 1 ); + usermessages->Register( "RequestState", 0 ); + + usermessages->Register( "BarTime", -1 ); // For the C4 progress bar. + usermessages->Register( "Damage", -1 ); // for HUD damage indicators + usermessages->Register( "RadioText", -1 ); // for HUD damage indicators + usermessages->Register( "HintText", -1 ); // Displays hint text display + usermessages->Register( "KeyHintText", -1 ); // Displays hint text display + + usermessages->Register( "ReloadEffect", 2 ); // a player reloading.. + usermessages->Register( "PlayerAnimEvent", -1 ); // jumping, firing, reload, etc. + + usermessages->Register( "HudMsg", -1 ); + + usermessages->Register( "VoiceSubtitle", 3 ); + usermessages->Register( "HandSignalSubtitle", 2 ); + usermessages->Register( "UpdateRadar", -1 ); + usermessages->Register( "KillCam", -1 ); + usermessages->Register( "DeathStats", 9 ); + + usermessages->Register( "AchievementEvent", -1 ); + usermessages->Register( "DODPlayerStatsUpdate", -1 ); + + // Voting + usermessages->Register( "CallVoteFailed", -1 ); + usermessages->Register( "VoteStart", -1 ); + usermessages->Register( "VotePass", -1 ); + usermessages->Register( "VoteFailed", 2 ); + usermessages->Register( "VoteSetup", -1 ); // Initiates client-side voting UI + + // NVNT register haptic user messages + RegisterHapticMessages(); +} + diff --git a/game/shared/dod/dod_viewmodel.cpp b/game/shared/dod/dod_viewmodel.cpp new file mode 100644 index 0000000..d353a50 --- /dev/null +++ b/game/shared/dod/dod_viewmodel.cpp @@ -0,0 +1,151 @@ + +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= +#include "cbase.h" +#include "dod_viewmodel.h" + +#ifdef CLIENT_DLL +#include "c_dod_player.h" +#include "prediction.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +LINK_ENTITY_TO_CLASS( dod_viewmodel, CDODViewModel ); + +IMPLEMENT_NETWORKCLASS_ALIASED( DODViewModel, DT_DODViewModel ) + +BEGIN_NETWORK_TABLE( CDODViewModel, DT_DODViewModel ) +END_NETWORK_TABLE() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +#ifdef CLIENT_DLL +CDODViewModel::CDODViewModel() : m_LagAnglesHistory("CDODViewModel::m_LagAnglesHistory") +{ + m_vLagAngles.Init(); + m_LagAnglesHistory.Setup( &m_vLagAngles, 0 ); + m_vLoweredWeaponOffset.Init(); +} +#else +CDODViewModel::CDODViewModel() +{ +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CDODViewModel::~CDODViewModel() +{ +} + +#ifdef CLIENT_DLL +ConVar cl_wpn_sway_interp( "cl_wpn_sway_interp", "0.1", FCVAR_CLIENTDLL ); +ConVar cl_wpn_sway_scale( "cl_wpn_sway_scale", "2.6", FCVAR_CLIENTDLL ); +#endif + +void CDODViewModel::CalcViewModelLag( Vector& origin, QAngle& angles, QAngle& original_angles ) +{ +#ifdef CLIENT_DLL + if ( prediction->InPrediction() ) + { + return; + } + + float flSwayScale = cl_wpn_sway_scale.GetFloat(); + + CWeaponDODBase *pWeapon = dynamic_cast<CWeaponDODBase*>(GetWeapon()); + + if ( pWeapon ) + { + flSwayScale *= pWeapon->GetViewModelSwayScale(); + } + + // Calculate our drift + Vector forward, right, up; + AngleVectors( angles, &forward, &right, &up ); + + // Add an entry to the history. + m_vLagAngles = angles; + m_LagAnglesHistory.NoteChanged( gpGlobals->curtime, cl_wpn_sway_interp.GetFloat(), false ); + + // Interpolate back 100ms. + m_LagAnglesHistory.Interpolate( gpGlobals->curtime, cl_wpn_sway_interp.GetFloat() ); + + // Now take the 100ms angle difference and figure out how far the forward vector moved in local space. + Vector vLaggedForward; + QAngle angleDiff = m_vLagAngles - angles; + AngleVectors( -angleDiff, &vLaggedForward, 0, 0 ); + Vector vForwardDiff = Vector(1,0,0) - vLaggedForward; + + // Now offset the origin using that. + vForwardDiff *= flSwayScale; + origin += forward*vForwardDiff.x + right*-vForwardDiff.y + up*vForwardDiff.z; +#endif +} + +#ifdef CLIENT_DLL +ConVar cl_gunlowerangle( "cl_gunlowerangle", "30", FCVAR_CLIENTDLL ); +ConVar cl_gunlowerspeed( "cl_gunlowerspeed", "2", FCVAR_CLIENTDLL ); + +ConVar cl_test_vm_offset( "cl_test_vm_offset", "0 0 0", FCVAR_CHEAT | FCVAR_CLIENTDLL ); +#endif + +void CDODViewModel::CalcViewModelView( CBasePlayer *owner, const Vector& eyePosition, const QAngle& eyeAngles ) +{ +#if defined( CLIENT_DLL ) + + Vector vecNewOrigin = eyePosition; + QAngle vecNewAngles = eyeAngles; + + // Check for lowering the weapon + C_DODPlayer *pPlayer = ToDODPlayer( owner ); + + Assert( pPlayer ); + + bool bLowered = pPlayer->IsWeaponLowered(); + + QAngle vecLoweredAngles(0,0,0); + + m_vLoweredWeaponOffset.x = Approach( bLowered ? cl_gunlowerangle.GetFloat() : 0, m_vLoweredWeaponOffset.x, cl_gunlowerspeed.GetFloat() ); + vecLoweredAngles.x += m_vLoweredWeaponOffset.x; + + vecNewAngles += vecLoweredAngles; + + Vector forward, right, up; + AngleVectors( vecNewAngles, &forward, &right, &up ); + + Vector test; + const char *szTestOffset = cl_test_vm_offset.GetString(); + sscanf( szTestOffset, " %f %f %f", &test[0], &test[1], &test[2] ); + + // cvar cl_test_vm_offset overrides calculated view model offset + if ( test.Length() > 0 ) + { + vecNewOrigin += forward * test[0] + right * test[1] + up * test[2]; + } + else + { + // Move the view model origin between the script standing and prone position + // based on the current view height + CWeaponDODBase *pWeapon = dynamic_cast<CWeaponDODBase*>(GetWeapon()); + + if ( pWeapon ) + { + Vector offset = pWeapon->GetDesiredViewModelOffset( pPlayer ); + + // add our offset in the proper direction + vecNewOrigin += forward * offset[0] + right * offset[1] + up * offset[2]; + } + } + + BaseClass::CalcViewModelView( owner, vecNewOrigin, vecNewAngles ); + +#endif +}
\ No newline at end of file diff --git a/game/shared/dod/dod_viewmodel.h b/game/shared/dod/dod_viewmodel.h new file mode 100644 index 0000000..4192a78 --- /dev/null +++ b/game/shared/dod/dod_viewmodel.h @@ -0,0 +1,60 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#ifndef DOD_VIEWMODEL_H +#define DOD_VIEWMODEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include "predictable_entity.h" +#include "utlvector.h" +#include "baseplayer_shared.h" +#include "shared_classnames.h" + +#if defined( CLIENT_DLL ) +#define CDODViewModel C_DODViewModel +#endif + +class CDODViewModel : public CBaseViewModel +{ + DECLARE_CLASS( CDODViewModel, CBaseViewModel ); +public: + + DECLARE_NETWORKCLASS(); + + CDODViewModel( void ); + ~CDODViewModel( void ); + + virtual void CalcViewModelLag( Vector& origin, QAngle& angles, QAngle& original_angles ); + virtual void CalcViewModelView( CBasePlayer *owner, const Vector& eyePosition, const QAngle& eyeAngles ); + +#if defined( CLIENT_DLL ) + virtual bool ShouldPredict( void ) + { + if ( GetOwner() && GetOwner() == C_BasePlayer::GetLocalPlayer() ) + return true; + + return BaseClass::ShouldPredict(); + } +#endif + +private: + +#if defined( CLIENT_DLL ) + + // This is used to lag the angles. + CInterpolatedVar<QAngle> m_LagAnglesHistory; + QAngle m_vLagAngles; + + CDODViewModel( const CDODViewModel & ); // not defined, not accessible + + QAngle m_vLoweredWeaponOffset; +#endif +}; + +#endif // DOD_VIEWMODEL_H diff --git a/game/shared/dod/dod_weapon_parse.cpp b/game/shared/dod/dod_weapon_parse.cpp new file mode 100644 index 0000000..35d7183 --- /dev/null +++ b/game/shared/dod/dod_weapon_parse.cpp @@ -0,0 +1,195 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include <KeyValues.h> +#include "dod_weapon_parse.h" +#include "dod_shareddefs.h" + +// criteria that we parse out of the file. +// tells us which player animation states +// should use the alternate wpn p model +static struct +{ + const char *m_pCriteriaName; + int m_iFlagValue; +} g_AltWpnCritera[] = +{ + { "ALTWPN_CRITERIA_FIRING", ALTWPN_CRITERIA_FIRING }, + { "ALTWPN_CRITERIA_RELOADING", ALTWPN_CRITERIA_RELOADING }, + { "ALTWPN_CRITERIA_DEPLOYED", ALTWPN_CRITERIA_DEPLOYED }, + { "ALTWPN_CRITERIA_DEPLOYED_RELOAD", ALTWPN_CRITERIA_DEPLOYED_RELOAD }, + { "ALTWPN_CRITERIA_PRONE_DEPLOYED_RELOAD", ALTWPN_CRITERIA_PRONE_DEPLOYED_RELOAD } +}; + +FileWeaponInfo_t* CreateWeaponInfo() +{ + return new CDODWeaponInfo; +} + + +CDODWeaponInfo::CDODWeaponInfo() +{ + m_szReloadModel[0] = '\0'; + m_szDeployedModel[0] = '\0'; + m_szDeployedReloadModel[0] = '\0'; + m_szProneDeployedReloadModel[0] = '\0'; +} + + +void CDODWeaponInfo::Parse( KeyValues *pKeyValuesData, const char *szWeaponName ) +{ + BaseClass::Parse( pKeyValuesData, szWeaponName ); + + m_iCrosshairMinDistance = pKeyValuesData->GetInt( "CrosshairMinDistance", 4 ); + m_iCrosshairDeltaDistance = pKeyValuesData->GetInt( "CrosshairDeltaDistance", 3 ); + m_iMuzzleFlashType = pKeyValuesData->GetFloat( "MuzzleFlashType", 0 ); + m_flMuzzleFlashScale = pKeyValuesData->GetFloat( "MuzzleFlashScale", 0.5 ); + + m_iDamage = pKeyValuesData->GetInt( "Damage", 1 ); + m_flAccuracy = pKeyValuesData->GetFloat( "Accuracy", 1.0 ); + m_flSecondaryAccuracy = pKeyValuesData->GetFloat( "SecondaryAccuracy", 1.0 ); + m_flAccuracyMovePenalty = pKeyValuesData->GetFloat( "AccuracyMovePenalty", 0.1 ); + m_flRecoil = pKeyValuesData->GetFloat( "Recoil", 99.0 ); + m_flPenetration = pKeyValuesData->GetFloat( "Penetration", 1.0 ); + m_flFireDelay = pKeyValuesData->GetFloat( "FireDelay", 0.1 ); + m_flSecondaryFireDelay = pKeyValuesData->GetFloat( "SecondaryFireDelay", 0.1 ); + m_flTimeToIdleAfterFire = pKeyValuesData->GetFloat( "IdleTimeAfterFire", 1.0 ); + m_flIdleInterval = pKeyValuesData->GetFloat( "IdleInterval", 1.0 ); + m_bCanDrop = ( pKeyValuesData->GetInt( "CanDrop", 1 ) > 0 ); + m_iBulletsPerShot = pKeyValuesData->GetInt( "BulletsPerShot", 1 ); + + m_iHudClipHeight = pKeyValuesData->GetInt( "HudClipHeight", 0 ); + m_iHudClipBaseHeight = pKeyValuesData->GetInt( "HudClipBaseHeight", 0 ); + m_iHudClipBulletHeight = pKeyValuesData->GetInt( "HudClipBulletHeight", 0 ); + + m_iAmmoPickupClips = pKeyValuesData->GetInt( "AmmoPickupClips", 2 ); + + m_iDefaultAmmoClips = pKeyValuesData->GetInt( "DefaultAmmoClips", 0 ); + + m_flViewModelFOV = pKeyValuesData->GetFloat( "ViewModelFOV", 90.0f ); + +// const char *pAnimEx = pKeyValuesData->GetString( "PlayerAnimationExtension", "error" ); +// Q_strncpy( m_szAnimExtension, pAnimEx, sizeof( m_szAnimExtension ) ); + + // if this key exists, use this for reload animations instead of anim_prefix +// Q_strncpy( m_szReloadAnimPrefix, pKeyValuesData->GetString( "reload_anim_prefix", "" ), MAX_WEAPON_PREFIX ); + + m_flBotAudibleRange = pKeyValuesData->GetFloat( "BotAudibleRange", 2000.0f ); + + m_iTracerType = pKeyValuesData->GetInt( "Tracer", 0 ); + + //Weapon Type + const char *pTypeString = pKeyValuesData->GetString( "WeaponType", NULL ); + + m_WeaponType = WPN_TYPE_UNKNOWN; + if ( !pTypeString ) + { + Assert( false ); + } + else if ( Q_stricmp( pTypeString, "Melee" ) == 0 ) + { + m_WeaponType = WPN_TYPE_MELEE; + } + else if ( Q_stricmp( pTypeString, "Camera" ) == 0 ) + { + m_WeaponType = WPN_TYPE_CAMERA; + } + else if ( Q_stricmp( pTypeString, "Grenade" ) == 0 ) + { + m_WeaponType = WPN_TYPE_GRENADE; + } + else if ( Q_stricmp( pTypeString, "Pistol" ) == 0 ) + { + m_WeaponType = WPN_TYPE_PISTOL; + } + else if ( Q_stricmp( pTypeString, "Rifle" ) == 0 ) + { + m_WeaponType = WPN_TYPE_RIFLE; + } + else if ( Q_stricmp( pTypeString, "Sniper" ) == 0 ) + { + m_WeaponType = WPN_TYPE_SNIPER; + } + else if ( Q_stricmp( pTypeString, "SubMG" ) == 0 ) + { + m_WeaponType = WPN_TYPE_SUBMG; + } + else if ( Q_stricmp( pTypeString, "MG" ) == 0 ) + { + m_WeaponType = WPN_TYPE_MG; + } + else if ( Q_stricmp( pTypeString, "Bazooka" ) == 0 ) + { + m_WeaponType = WPN_TYPE_BAZOOKA; + } + else if ( Q_stricmp( pTypeString, "Bandage" ) == 0 ) + { + m_WeaponType = WPN_TYPE_BANDAGE; + } + else if ( Q_stricmp( pTypeString, "Sidearm" ) == 0 ) + { + m_WeaponType = WPN_TYPE_SIDEARM; + } + else if ( Q_stricmp( pTypeString, "RifleGrenade" ) == 0 ) + { + m_WeaponType = WPN_TYPE_RIFLEGRENADE; + } + else if ( Q_stricmp( pTypeString, "Bomb" ) == 0 ) + { + m_WeaponType = WPN_TYPE_BOMB; + } + else + { + Assert( false ); + } + + Q_strncpy( m_szReloadModel, pKeyValuesData->GetString( "reloadmodel" ), sizeof( m_szReloadModel ) ); + Q_strncpy( m_szDeployedModel, pKeyValuesData->GetString( "deployedmodel" ), sizeof( m_szDeployedModel ) ); + Q_strncpy( m_szDeployedReloadModel, pKeyValuesData->GetString( "deployedreloadmodel" ), sizeof( m_szDeployedReloadModel ) ); + Q_strncpy( m_szProneDeployedReloadModel, pKeyValuesData->GetString( "pronedeployedreloadmodel" ), sizeof( m_szProneDeployedReloadModel ) ); + + + m_iAltWpnCriteria = ALTWPN_CRITERIA_NONE; + + for ( int i=0; i < ARRAYSIZE( g_AltWpnCritera ); i++ ) + { + int iVal = pKeyValuesData->GetInt( g_AltWpnCritera[i].m_pCriteriaName, 0 ); + if ( iVal == 1 ) + { + m_iAltWpnCriteria |= g_AltWpnCritera[i].m_iFlagValue; + } + } + + const char *szNormalOffset = pKeyValuesData->GetString( "vm_normal_offset", "0 0 0" ); + const char *szProneOffset = pKeyValuesData->GetString( "vm_prone_offset", "0 0 0" ); + const char *szIronSightOffset = pKeyValuesData->GetString( "vm_ironsight_offset", "0 0 0" ); + + sscanf( szNormalOffset, "%f %f %f", &m_vecViewNormalOffset[0], &m_vecViewNormalOffset[1], &m_vecViewNormalOffset[2]); + sscanf( szProneOffset, "%f %f %f", &m_vecViewProneOffset[0], &m_vecViewProneOffset[1], &m_vecViewProneOffset[2]); + sscanf( szIronSightOffset, "%f %f %f", &m_vecIronSightOffset[0], &m_vecIronSightOffset[1], &m_vecIronSightOffset[2]); + + m_iDefaultTeam = TEAM_ALLIES; + + const char *pDefaultTeam = pKeyValuesData->GetString( "default_team", NULL ); + + if ( pDefaultTeam ) + { + if ( FStrEq( pDefaultTeam, "Axis" ) ) + { + m_iDefaultTeam = TEAM_AXIS; + } + else if ( FStrEq( pDefaultTeam, "Allies" ) ) + { + m_iDefaultTeam = TEAM_ALLIES; + } + else + { + Assert( !"invalid param to \"default_team\" in weapon scripts\n" ); + } + } +} + diff --git a/game/shared/dod/dod_weapon_parse.h b/game/shared/dod/dod_weapon_parse.h new file mode 100644 index 0000000..a42eb42 --- /dev/null +++ b/game/shared/dod/dod_weapon_parse.h @@ -0,0 +1,103 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef DOD_WEAPON_PARSE_H +#define DOD_WEAPON_PARSE_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "weapon_parse.h" +#include "networkvar.h" + +#define WPN_TYPE_MELEE (1<<0) +#define WPN_TYPE_GRENADE (1<<1) +//#define WPN_TYPE_GRENADE_LIVE (1<<2) //exploding grenades, unused +#define WPN_TYPE_PISTOL (1<<3) +#define WPN_TYPE_RIFLE (1<<4) +#define WPN_TYPE_SNIPER (1<<5) +#define WPN_TYPE_SUBMG (1<<6) +#define WPN_TYPE_MG (1<<7) //mg42, 30cal +#define WPN_TYPE_BAZOOKA (1<<8) +#define WPN_TYPE_BANDAGE (1<<9) +#define WPN_TYPE_SIDEARM (1<<10) //carbine - secondary weapons +#define WPN_TYPE_RIFLEGRENADE (1<<11) +#define WPN_TYPE_BOMB (1<<12) +#define WPN_TYPE_UNKNOWN (1<<13) +#define WPN_TYPE_CAMERA (1<<12) + +#define WPN_MASK_GUN ( WPN_TYPE_PISTOL | WPN_TYPE_RIFLE | WPN_TYPE_SNIPER | WPN_TYPE_SUBMG | WPN_TYPE_MG | WPN_TYPE_SIDEARM ) + +//-------------------------------------------------------------------------------------------------------- +class CDODWeaponInfo : public FileWeaponInfo_t +{ +public: + DECLARE_CLASS_GAMEROOT( CDODWeaponInfo, FileWeaponInfo_t ); + + CDODWeaponInfo(); + + virtual void Parse( ::KeyValues *pKeyValuesData, const char *szWeaponName ); + + int m_iDamage; + int m_flPenetration; + int m_iBulletsPerShot; + int m_iMuzzleFlashType; + float m_flMuzzleFlashScale; + + bool m_bCanDrop; + + float m_flRecoil; + + float m_flRange; + float m_flRangeModifier; + + float m_flAccuracy; + float m_flSecondaryAccuracy; + float m_flAccuracyMovePenalty; + + float m_flFireDelay; + float m_flSecondaryFireDelay; + + int m_iCrosshairMinDistance; + int m_iCrosshairDeltaDistance; + + int m_WeaponType; + + float m_flBotAudibleRange; + + char m_szReloadModel[MAX_WEAPON_STRING]; + char m_szDeployedModel[MAX_WEAPON_STRING]; + char m_szDeployedReloadModel[MAX_WEAPON_STRING]; + char m_szProneDeployedReloadModel[MAX_WEAPON_STRING]; + + //timers + float m_flTimeToIdleAfterFire; //wait this long until idling after fire + float m_flIdleInterval; //wait this long after idling to idle again + + //ammo + int m_iDefaultAmmoClips; + int m_iAmmoPickupClips; + + int m_iHudClipHeight; + int m_iHudClipBaseHeight; + int m_iHudClipBulletHeight; + + int m_iTracerType; + + float m_flViewModelFOV; + + int m_iAltWpnCriteria; + + Vector m_vecViewNormalOffset; + Vector m_vecViewProneOffset; + Vector m_vecIronSightOffset; + + int m_iDefaultTeam; +}; + + +#endif // DOD_WEAPON_PARSE_H diff --git a/game/shared/dod/fx_dod_shared.cpp b/game/shared/dod/fx_dod_shared.cpp new file mode 100644 index 0000000..1590012 --- /dev/null +++ b/game/shared/dod/fx_dod_shared.cpp @@ -0,0 +1,370 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "fx_dod_shared.h" +#include "weapon_dodbase.h" +#include "engine/ivdebugoverlay.h" + +#ifndef CLIENT_DLL + #include "ilagcompensationmanager.h" +#endif + +#ifndef CLIENT_DLL + +//============================================================================= +// +// Explosions. +// +class CTEDODExplosion : public CBaseTempEntity +{ +public: + + DECLARE_CLASS( CTEDODExplosion, CBaseTempEntity ); + DECLARE_SERVERCLASS(); + + CTEDODExplosion( const char *name ); + +public: + + Vector m_vecOrigin; + Vector m_vecNormal; +}; + +// Singleton to fire explosion objects +static CTEDODExplosion g_TEDODExplosion( "DODExplosion" ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEDODExplosion::CTEDODExplosion( const char *name ) : CBaseTempEntity( name ) +{ + m_vecOrigin.Init(); + m_vecNormal.Init(); +} + +IMPLEMENT_SERVERCLASS_ST( CTEDODExplosion, DT_TEDODExplosion ) + SendPropFloat( SENDINFO_NOCHECK( m_vecOrigin[0] ), -1, SPROP_COORD_MP_INTEGRAL ), + SendPropFloat( SENDINFO_NOCHECK( m_vecOrigin[1] ), -1, SPROP_COORD_MP_INTEGRAL ), + SendPropFloat( SENDINFO_NOCHECK( m_vecOrigin[2] ), -1, SPROP_COORD_MP_INTEGRAL ), + SendPropVector( SENDINFO_NOCHECK( m_vecNormal ), 6, 0, -1.0f, 1.0f ), +END_SEND_TABLE() + +void TE_DODExplosion( IRecipientFilter &filter, float flDelay, const Vector &vecOrigin, const Vector &vecNormal ) +{ + VectorCopy( vecOrigin, g_TEDODExplosion.m_vecOrigin ); + VectorCopy( vecNormal, g_TEDODExplosion.m_vecNormal ); + + // Send it over the wire + g_TEDODExplosion.Create( filter, flDelay ); +} + +#endif + +#ifdef CLIENT_DLL + + #include "fx_impact.h" + + extern void FX_TracerSound( const Vector &start, const Vector &end, int iTracerType ); + + // this is a cheap ripoff from CBaseCombatWeapon::WeaponSound(): + void FX_WeaponSound( + int iPlayerIndex, + WeaponSound_t sound_type, + const Vector &vOrigin, + CDODWeaponInfo *pWeaponInfo ) + { + + // If we have some sounds from the weapon classname.txt file, play a random one of them + const char *shootsound = pWeaponInfo->aShootSounds[ sound_type ]; + if ( !shootsound || !shootsound[0] ) + return; + + CBroadcastRecipientFilter filter; // this is client side only + + if ( !te->CanPredict() ) + return; + + CBaseEntity::EmitSound( filter, iPlayerIndex, shootsound, &vOrigin ); + } + + class CGroupedSound + { + public: + string_t m_SoundName; + Vector m_vPos; + }; + + CUtlVector<CGroupedSound> g_GroupedSounds; + + + // Called by the ImpactSound function. + void ShotgunImpactSoundGroup( const char *pSoundName, const Vector &vEndPos ) + { + // Don't play the sound if it's too close to another impact sound. + for ( int i=0; i < g_GroupedSounds.Count(); i++ ) + { + CGroupedSound *pSound = &g_GroupedSounds[i]; + + if ( vEndPos.DistToSqr( pSound->m_vPos ) < 300*300 ) + { + if ( Q_stricmp( pSound->m_SoundName, pSoundName ) == 0 ) + return; + } + } + + // Ok, play the sound and add it to the list. + CLocalPlayerFilter filter; + C_BaseEntity::EmitSound( filter, NULL, pSoundName, &vEndPos ); + + int tail = g_GroupedSounds.AddToTail(); + g_GroupedSounds[tail].m_SoundName = pSoundName; + g_GroupedSounds[tail].m_vPos = vEndPos; + } + + + void StartGroupingSounds() + { + Assert( g_GroupedSounds.Count() == 0 ); + SetImpactSoundRoute( ShotgunImpactSoundGroup ); + } + + + void EndGroupingSounds() + { + g_GroupedSounds.Purge(); + SetImpactSoundRoute( NULL ); + } + +#else + + #include "te_firebullets.h" + + // Server doesn't play sounds anyway. + void StartGroupingSounds() {} + void EndGroupingSounds() {} + void FX_WeaponSound ( int iPlayerIndex, + WeaponSound_t sound_type, + const Vector &vOrigin, + CDODWeaponInfo *pWeaponInfo ) {}; + +#endif + + + +// This runs on both the client and the server. +// On the server, it only does the damage calculations. +// On the client, it does all the effects. +void FX_FireBullets( + int iPlayerIndex, + const Vector &vOrigin, + const QAngle &vAngles, + int iWeaponID, + int iMode, + int iSeed, + float flSpread + ) +{ + bool bDoEffects = true; + +#ifdef CLIENT_DLL + C_DODPlayer *pPlayer = ToDODPlayer( ClientEntityList().GetBaseEntity( iPlayerIndex ) ); +#else + CDODPlayer *pPlayer = ToDODPlayer( UTIL_PlayerByIndex( iPlayerIndex) ); +#endif + + const char * weaponAlias = WeaponIDToAlias( iWeaponID ); + + if ( !weaponAlias ) + { + DevMsg("FX_FireBullets: weapon alias for ID %i not found\n", iWeaponID ); + return; + } + + //MATTTODO: Why are we looking up the weapon info again when every weapon + // stores its own m_pWeaponInfo pointer? + + char wpnName[128]; + Q_snprintf( wpnName, sizeof( wpnName ), "weapon_%s", weaponAlias ); + WEAPON_FILE_INFO_HANDLE hWpnInfo = LookupWeaponInfoSlot( wpnName ); + + if ( hWpnInfo == GetInvalidWeaponInfoHandle() ) + { + DevMsg("FX_FireBullets: LookupWeaponInfoSlot failed for weapon %s\n", wpnName ); + return; + } + + CDODWeaponInfo *pWeaponInfo = static_cast< CDODWeaponInfo* >( GetFileWeaponInfoFromHandle( hWpnInfo ) ); + +#ifdef CLIENT_DLL + if( pPlayer && !pPlayer->IsDormant() ) + pPlayer->DoAnimationEvent( PLAYERANIMEVENT_FIRE_GUN ); +#else + if( pPlayer ) + pPlayer->DoAnimationEvent( PLAYERANIMEVENT_FIRE_GUN ); +#endif + +#ifndef CLIENT_DLL + // if this is server code, send the effect over to client as temp entity + // Dispatch one message for all the bullet impacts and sounds. + TE_FireBullets( + iPlayerIndex, + vOrigin, + vAngles, + iWeaponID, + iMode, + iSeed, + flSpread + ); + + bDoEffects = false; // no effects on server + + // Let the player remember the usercmd he fired a weapon on. Assists in making decisions about lag compensation. + pPlayer->NoteWeaponFired(); +#endif + + + + WeaponSound_t sound_type = SINGLE; + + if ( bDoEffects) + { + FX_WeaponSound( iPlayerIndex, sound_type, vOrigin, pWeaponInfo ); + } + + // Fire bullets, calculate impacts & effects + if ( !pPlayer ) + return; + + StartGroupingSounds(); + +#if !defined (CLIENT_DLL) + // Move other players back to history positions based on local player's lag + lagcompensation->StartLagCompensation( pPlayer, pPlayer->GetCurrentCommand() ); +#endif + + RandomSeed( iSeed ); + + float x, y; + do + { + x = random->RandomFloat( -0.5, 0.5 ) + random->RandomFloat( -0.5, 0.5 ); + y = random->RandomFloat( -0.5, 0.5 ) + random->RandomFloat( -0.5, 0.5 ); + } while ( (x * x + y * y) > 1.0f ); + + Vector vecForward, vecRight, vecUp; + AngleVectors( vAngles, &vecForward, &vecRight, &vecUp ); + + Vector vecDirShooting = vecForward + + x * flSpread * vecRight + + y * flSpread * vecUp; + + vecDirShooting.NormalizeInPlace(); + + FireBulletsInfo_t info( 1 /*shots*/, vOrigin, vecDirShooting, Vector( flSpread, flSpread, FLOAT32_NAN), MAX_COORD_RANGE, pWeaponInfo->iAmmoType ); + info.m_flDamage = pWeaponInfo->m_iDamage; + info.m_pAttacker = pPlayer; + + pPlayer->FireBullets( info ); + +#ifdef CLIENT_DLL + + { + trace_t tr; + UTIL_TraceLine( vOrigin, vOrigin + vecDirShooting * MAX_COORD_RANGE, MASK_SOLID, pPlayer, COLLISION_GROUP_NONE, &tr ); + + // if this is a local player, start at attachment on view model + // else start on attachment on weapon model + + int iEntIndex = pPlayer->entindex(); + int iAttachment = 1; + + Vector vecStart = tr.startpos; + QAngle angAttachment; + + C_DODPlayer *pLocalPlayer = C_DODPlayer::GetLocalDODPlayer(); + + bool bInToolRecordingMode = clienttools->IsInRecordingMode(); + + // try to align tracers to actual weapon barrel if possible + if ( pPlayer->IsLocalPlayer() && !bInToolRecordingMode ) + { + C_BaseViewModel *pViewModel = pPlayer->GetViewModel(0); + + if ( pViewModel ) + { + iEntIndex = pViewModel->entindex(); + pViewModel->GetAttachment( iAttachment, vecStart, angAttachment ); + } + } + else if ( pLocalPlayer && + pLocalPlayer->GetObserverTarget() == pPlayer && + pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE ) + { + // get our observer target's view model + + C_BaseViewModel *pViewModel = pLocalPlayer->GetViewModel(0); + + if ( pViewModel ) + { + iEntIndex = pViewModel->entindex(); + pViewModel->GetAttachment( iAttachment, vecStart, angAttachment ); + } + } + else if ( !pPlayer->IsDormant() ) + { + // fill in with third person weapon model index + C_BaseCombatWeapon *pWeapon = pPlayer->GetActiveWeapon(); + + if( pWeapon ) + { + iEntIndex = pWeapon->entindex(); + + int nModelIndex = pWeapon->GetModelIndex(); + int nWorldModelIndex = pWeapon->GetWorldModelIndex(); + if ( bInToolRecordingMode && nModelIndex != nWorldModelIndex ) + { + pWeapon->SetModelIndex( nWorldModelIndex ); + } + + pWeapon->GetAttachment( iAttachment, vecStart, angAttachment ); + + if ( bInToolRecordingMode && nModelIndex != nWorldModelIndex ) + { + pWeapon->SetModelIndex( nModelIndex ); + } + } + } + + switch( pWeaponInfo->m_iTracerType ) + { + case 1: // Machine gun, heavy tracer + UTIL_Tracer( vecStart, tr.endpos, iEntIndex, TRACER_DONT_USE_ATTACHMENT, 5000.0, true, "BrightTracer" ); + break; + + case 2: // rifle, smg, light tracer + vecStart += vecDirShooting * 150; + UTIL_Tracer( vecStart, tr.endpos, iEntIndex, TRACER_DONT_USE_ATTACHMENT, 5000.0, true, "FaintTracer" ); + break; + + case 0: // pistols etc, just do the sound + { + FX_TracerSound( vecStart, tr.endpos, TRACER_TYPE_DEFAULT ); + } + default: + break; + } + } +#endif + +#if !defined (CLIENT_DLL) + lagcompensation->FinishLagCompensation( pPlayer ); +#endif + + EndGroupingSounds(); +} + diff --git a/game/shared/dod/fx_dod_shared.h b/game/shared/dod/fx_dod_shared.h new file mode 100644 index 0000000..2d74124 --- /dev/null +++ b/game/shared/dod/fx_dod_shared.h @@ -0,0 +1,44 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef FX_DOD_SHARED_H +#define FX_DOD_SHARED_H +#ifdef _WIN32 +#pragma once +#endif + + +#ifdef CLIENT_DLL + #include "c_dod_player.h" +#else + #include "dod_player.h" +#endif + +#include "dod_weapon_parse.h" + + +// This runs on both the client and the server. +// On the server, it only does the damage calculations. +// On the client, it does all the effects. +void FX_FireBullets( + int iPlayer, + const Vector &vOrigin, + const QAngle &vAngles, + int iWeaponID, + int iMode, + int iSeed, + float flSpread + ); + + +#ifndef CLIENT_DLL + + void TE_DODExplosion( IRecipientFilter &filter, float flDelay, const Vector &vecOrigin, const Vector &vecNormal ); + +#endif + + +#endif // FX_DOD_SHARED_H diff --git a/game/shared/dod/weapon_30cal.cpp b/game/shared/dod/weapon_30cal.cpp new file mode 100644 index 0000000..919f9f7 --- /dev/null +++ b/game/shared/dod/weapon_30cal.cpp @@ -0,0 +1,423 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodbipodgun.h" + +#if defined( CLIENT_DLL ) + + #include "c_dod_player.h" + + #define CWeapon30cal C_Weapon30cal + +#else + + #include "dod_player.h" + +#endif + + +class CWeapon30cal : public CDODBipodWeapon +{ +public: + DECLARE_CLASS( CWeapon30cal, CDODBipodWeapon ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeapon30cal() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_30CAL; } + + // weapon id for stats purposes + virtual DODWeaponID GetStatsWeaponID( void ) + { + if ( !IsDeployed() ) + return WEAPON_30CAL_UNDEPLOYED; + else + return WEAPON_30CAL; + } + + virtual void PrimaryAttack( void ); + + virtual bool ShouldDrawCrosshair( void ) { return IsDeployed(); } + + virtual bool Reload( void ); + + virtual Activity GetDrawActivity( void ); + virtual Activity GetDeployActivity( void ); + virtual Activity GetUndeployActivity( void ); + virtual Activity GetIdleActivity( void ); + virtual Activity GetPrimaryAttackActivity( void ); + + virtual float GetRecoil( void ); + +private: + CWeapon30cal( const CWeapon30cal & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( Weapon30cal, DT_Weapon30cal ) + +BEGIN_NETWORK_TABLE( CWeapon30cal, DT_Weapon30cal ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeapon30cal ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_30cal, CWeapon30cal ); +PRECACHE_WEAPON_REGISTER( weapon_30cal ); + +acttable_t CWeapon30cal::m_acttable[] = +{ + { ACT_DOD_STAND_AIM, ACT_DOD_STAND_AIM_30CAL, false }, + { ACT_DOD_CROUCH_AIM, ACT_DOD_CROUCH_AIM_30CAL, false }, + { ACT_DOD_CROUCHWALK_AIM, ACT_DOD_CROUCHWALK_AIM_30CAL, false }, + { ACT_DOD_WALK_AIM, ACT_DOD_WALK_AIM_30CAL, false }, + { ACT_DOD_RUN_AIM, ACT_DOD_RUN_AIM_30CAL, false }, + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_30CAL, false }, + { ACT_PRONE_FORWARD, ACT_DOD_PRONEWALK_IDLE_30CAL, false }, + { ACT_DOD_STAND_IDLE, ACT_DOD_STAND_IDLE_30CAL, false }, + { ACT_DOD_CROUCH_IDLE, ACT_DOD_CROUCH_IDLE_30CAL, false }, + { ACT_DOD_CROUCHWALK_IDLE, ACT_DOD_CROUCHWALK_IDLE_30CAL, false }, + { ACT_DOD_WALK_IDLE, ACT_DOD_WALK_IDLE_30CAL, false }, + { ACT_DOD_RUN_IDLE, ACT_DOD_RUN_IDLE_30CAL, false }, + { ACT_SPRINT, ACT_DOD_SPRINT_IDLE_30CAL, false }, + + { ACT_RANGE_ATTACK1, ACT_DOD_PRIMARYATTACK_30CAL, false }, + { ACT_DOD_PRIMARYATTACK_CROUCH, ACT_DOD_PRIMARYATTACK_30CAL, false }, + { ACT_DOD_PRIMARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_30CAL, false }, + { ACT_DOD_DEPLOYED, ACT_DOD_DEPLOY_30CAL, false }, + { ACT_DOD_PRONE_DEPLOYED, ACT_DOD_PRONE_DEPLOY_30CAL, false }, + { ACT_DOD_PRIMARYATTACK_DEPLOYED, ACT_DOD_PRIMARYATTACK_DEPLOYED_30CAL, false }, + { ACT_DOD_PRIMARYATTACK_PRONE_DEPLOYED, ACT_DOD_PRIMARYATTACK_PRONE_DEPLOYED_30CAL,false }, + { ACT_DOD_RELOAD_DEPLOYED, ACT_DOD_RELOAD_DEPLOYED_30CAL, false }, + { ACT_DOD_RELOAD_PRONE_DEPLOYED, ACT_DOD_RELOAD_PRONE_DEPLOYED_30CAL, false }, + + { ACT_DOD_HS_IDLE, ACT_DOD_HS_IDLE_30CAL, false }, + { ACT_DOD_HS_CROUCH, ACT_DOD_HS_CROUCH_30CAL, false }, +}; + +IMPLEMENT_ACTTABLE( CWeapon30cal ); + +bool CWeapon30cal::Reload( void ) +{ + if( !IsDeployed() ) + { +#ifdef CLIENT_DLL + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + + if ( pPlayer ) + pPlayer->HintMessage( HINT_MG_DEPLOY_TO_RELOAD ); +#endif + return false; + } + + return BaseClass::Reload(); +} + +void CWeapon30cal::PrimaryAttack( void ) +{ + if ( m_iClip1 <= 0 ) + { + if (m_bFireOnEmpty) + { + PlayEmptySound(); + m_flNextPrimaryAttack = gpGlobals->curtime + 0.2; + } + + return; + } + +#ifdef CLIENT_DLL + C_DODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); +#else + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); +#endif + + Assert( pPlayer ); + + if( !IsDeployed() ) + { +#ifdef CLIENT_DLL + pPlayer->HintMessage( HINT_MG_FIRE_UNDEPLOYED ); +#endif + pPlayer->m_Shared.SetSlowedTime( 0.2 ); + + float flStamina = pPlayer->m_Shared.GetStamina(); + + pPlayer->m_Shared.SetStamina( flStamina - 15 ); + } + + BaseClass::PrimaryAttack(); +} + +Activity CWeapon30cal::GetDrawActivity( void ) +{ + Activity actDraw; + + if( m_iClip1 <= 0 ) + actDraw = ACT_VM_DRAW_EMPTY; + else + actDraw = ACT_VM_DRAW; + + return actDraw; +} + +Activity CWeapon30cal::GetDeployActivity( void ) +{ + Activity actDeploy; + + switch ( m_iClip1 ) + { + case 8: + actDeploy = ACT_VM_DEPLOY_8; + break; + case 7: + actDeploy = ACT_VM_DEPLOY_7; + break; + case 6: + actDeploy = ACT_VM_DEPLOY_6; + break; + case 5: + actDeploy = ACT_VM_DEPLOY_5; + break; + case 4: + actDeploy = ACT_VM_DEPLOY_4; + break; + case 3: + actDeploy = ACT_VM_DEPLOY_3; + break; + case 2: + actDeploy = ACT_VM_DEPLOY_2; + break; + case 1: + actDeploy = ACT_VM_DEPLOY_1; + break; + case 0: + actDeploy = ACT_VM_DEPLOY_EMPTY; + break; + default: + actDeploy = ACT_VM_DEPLOY; + break; + } + + return actDeploy; +} + +Activity CWeapon30cal::GetUndeployActivity( void ) +{ + Activity actUndeploy; + + switch ( m_iClip1 ) + { + case 8: + actUndeploy = ACT_VM_UNDEPLOY_8; + break; + case 7: + actUndeploy = ACT_VM_UNDEPLOY_7; + break; + case 6: + actUndeploy = ACT_VM_UNDEPLOY_6; + break; + case 5: + actUndeploy = ACT_VM_UNDEPLOY_5; + break; + case 4: + actUndeploy = ACT_VM_UNDEPLOY_4; + break; + case 3: + actUndeploy = ACT_VM_UNDEPLOY_3; + break; + case 2: + actUndeploy = ACT_VM_UNDEPLOY_2; + break; + case 1: + actUndeploy = ACT_VM_UNDEPLOY_1; + break; + case 0: + actUndeploy = ACT_VM_UNDEPLOY_EMPTY; + break; + default: + actUndeploy = ACT_VM_UNDEPLOY; + break; + } + + return actUndeploy; +} + +Activity CWeapon30cal::GetIdleActivity( void ) +{ + Activity actIdle; + + if( IsDeployed() ) + { + switch ( m_iClip1 ) + { + case 8: + actIdle = ACT_VM_IDLE_DEPLOYED_8; + break; + case 7: + actIdle = ACT_VM_IDLE_DEPLOYED_7; + break; + case 6: + actIdle = ACT_VM_IDLE_DEPLOYED_6; + break; + case 5: + actIdle = ACT_VM_IDLE_DEPLOYED_5; + break; + case 4: + actIdle = ACT_VM_IDLE_DEPLOYED_4; + break; + case 3: + actIdle = ACT_VM_IDLE_DEPLOYED_3; + break; + case 2: + actIdle = ACT_VM_IDLE_DEPLOYED_2; + break; + case 1: + actIdle = ACT_VM_IDLE_DEPLOYED_1; + break; + case 0: + actIdle = ACT_VM_IDLE_DEPLOYED_EMPTY; + break; + default: + actIdle = ACT_VM_IDLE_DEPLOYED; + break; + } + } + else + { + switch ( m_iClip1 ) + { + case 8: + actIdle = ACT_VM_IDLE_8; + break; + case 7: + actIdle = ACT_VM_IDLE_7; + break; + case 6: + actIdle = ACT_VM_IDLE_6; + break; + case 5: + actIdle = ACT_VM_IDLE_5; + break; + case 4: + actIdle = ACT_VM_IDLE_4; + break; + case 3: + actIdle = ACT_VM_IDLE_3; + break; + case 2: + actIdle = ACT_VM_IDLE_2; + break; + case 1: + actIdle = ACT_VM_IDLE_1; + break; + case 0: + actIdle = ACT_VM_IDLE_EMPTY; + break; + default: + actIdle = ACT_VM_IDLE; + break; + } + } + + return actIdle; +} + +Activity CWeapon30cal::GetPrimaryAttackActivity( void ) +{ + Activity actPrim; + + int maxhax = m_iClip1 + 2; + + if( IsDeployed() ) + { + switch ( maxhax ) + { + case 8: + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED_8; + break; + case 7: + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED_7; + break; + case 6: + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED_6; + break; + case 5: + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED_5; + break; + case 4: + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED_4; + break; + case 3: + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED_3; + break; + case 2: + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED_2; + break; + case 1: + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED_1; + break; + case 0: + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED_EMPTY; + break; + default: + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED; + break; + } + } + else + { + switch ( maxhax ) + { + case 8: + actPrim = ACT_VM_PRIMARYATTACK_8; + break; + case 7: + actPrim = ACT_VM_PRIMARYATTACK_7; + break; + case 6: + actPrim = ACT_VM_PRIMARYATTACK_6; + break; + case 5: + actPrim = ACT_VM_PRIMARYATTACK_5; + break; + case 4: + actPrim = ACT_VM_PRIMARYATTACK_4; + break; + case 3: + actPrim = ACT_VM_PRIMARYATTACK_3; + break; + case 2: + actPrim = ACT_VM_PRIMARYATTACK_2; + break; + case 1: + actPrim = ACT_VM_PRIMARYATTACK_1; + break; + case 0: + actPrim = ACT_VM_PRIMARYATTACK_EMPTY; + break; + default: + actPrim = ACT_VM_PRIMARYATTACK; + break; + } + } + + return actPrim; +} + +float CWeapon30cal::GetRecoil( void ) +{ + CDODPlayer *p = ToDODPlayer( GetPlayerOwner() ); + + if( p && p->m_Shared.IsInMGDeploy() ) + { + return 0.0f; + } + + return 20; +} diff --git a/game/shared/dod/weapon_amerknife.cpp b/game/shared/dod/weapon_amerknife.cpp new file mode 100644 index 0000000..685487d --- /dev/null +++ b/game/shared/dod/weapon_amerknife.cpp @@ -0,0 +1,77 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodbasemelee.h" +#include "dod_shareddefs.h" + +#if defined( CLIENT_DLL ) + + #define CWeaponAmerKnife C_WeaponAmerKnife + +#endif + + +class CWeaponAmerKnife : public CWeaponDODBaseMelee +{ +public: + DECLARE_CLASS( CWeaponAmerKnife, CWeaponDODBaseMelee ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeaponAmerKnife() {} + + virtual Activity GetMeleeActivity( void ) { return ACT_VM_PRIMARYATTACK; } + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_AMERKNIFE; } + + virtual void PrimaryAttack() + { + MeleeAttack( 60, MELEE_DMG_FIST, 0.2f, 0.4f ); + } + +private: + CWeaponAmerKnife( const CWeaponAmerKnife & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponAmerKnife, DT_WeaponAmerKnife ) + +BEGIN_NETWORK_TABLE( CWeaponAmerKnife, DT_WeaponAmerKnife ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponAmerKnife ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_amerknife, CWeaponAmerKnife ); +PRECACHE_WEAPON_REGISTER( weapon_amerknife ); + +acttable_t CWeaponAmerKnife::m_acttable[] = +{ + { ACT_DOD_STAND_AIM, ACT_DOD_STAND_AIM_KNIFE, false }, + { ACT_DOD_CROUCH_AIM, ACT_DOD_CROUCH_AIM_KNIFE, false }, + { ACT_DOD_CROUCHWALK_AIM, ACT_DOD_CROUCHWALK_AIM_KNIFE, false }, + { ACT_DOD_WALK_AIM, ACT_DOD_WALK_AIM_KNIFE, false }, + { ACT_DOD_RUN_AIM, ACT_DOD_RUN_AIM_KNIFE, false }, + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_KNIFE, false }, + { ACT_PRONE_FORWARD, ACT_DOD_PRONEWALK_AIM_KNIFE, false }, + { ACT_DOD_STAND_IDLE, ACT_DOD_STAND_AIM_KNIFE, false }, + { ACT_DOD_CROUCH_IDLE, ACT_DOD_CROUCH_AIM_KNIFE, false }, + { ACT_DOD_CROUCHWALK_IDLE, ACT_DOD_CROUCHWALK_AIM_KNIFE, false }, + { ACT_DOD_WALK_IDLE, ACT_DOD_WALK_AIM_KNIFE, false }, + { ACT_DOD_RUN_IDLE, ACT_DOD_RUN_AIM_KNIFE, false }, + { ACT_SPRINT, ACT_DOD_SPRINT_AIM_KNIFE, false }, + + { ACT_RANGE_ATTACK2, ACT_DOD_PRIMARYATTACK_KNIFE, false }, + { ACT_DOD_SECONDARYATTACK_CROUCH, ACT_DOD_PRIMARYATTACK_CROUCH_KNIFE, false }, + { ACT_DOD_SECONDARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_KNIFE, false }, + + // Hand Signals + { ACT_DOD_HS_IDLE, ACT_DOD_HS_IDLE_KNIFE, false }, + { ACT_DOD_HS_CROUCH, ACT_DOD_HS_CROUCH_KNIFE, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponAmerKnife ); diff --git a/game/shared/dod/weapon_bar.cpp b/game/shared/dod/weapon_bar.cpp new file mode 100644 index 0000000..a8a3539 --- /dev/null +++ b/game/shared/dod/weapon_bar.cpp @@ -0,0 +1,84 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodfireselect.h" + +#if defined( CLIENT_DLL ) + + #define CWeaponBAR C_WeaponBAR + +#endif + + +class CWeaponBAR : public CDODFireSelectWeapon +{ +public: + DECLARE_CLASS( CWeaponBAR, CDODFireSelectWeapon ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeaponBAR() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_BAR; } + + // weapon id for stats purposes + virtual DODWeaponID GetStatsWeaponID( void ) + { + if ( IsSemiAuto() ) + return WEAPON_BAR_SEMIAUTO; + else + return WEAPON_BAR; + } + + virtual float GetRecoil( void ) { return 5.0f; } + +private: + CWeaponBAR( const CWeaponBAR & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponBAR, DT_WeaponBAR ) + +BEGIN_NETWORK_TABLE( CWeaponBAR, DT_WeaponBAR ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponBAR ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_bar, CWeaponBAR ); +PRECACHE_WEAPON_REGISTER( weapon_bar ); + +acttable_t CWeaponBAR::m_acttable[] = +{ + { ACT_DOD_STAND_AIM, ACT_DOD_STAND_AIM_BAR, false }, + { ACT_DOD_CROUCH_AIM, ACT_DOD_CROUCH_AIM_BAR, false }, + { ACT_DOD_CROUCHWALK_AIM, ACT_DOD_CROUCHWALK_AIM_BAR, false }, + { ACT_DOD_WALK_AIM, ACT_DOD_WALK_AIM_BAR, false }, + { ACT_DOD_RUN_AIM, ACT_DOD_RUN_AIM_BAR, false }, + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_BAR, false }, + { ACT_PRONE_FORWARD, ACT_DOD_PRONEWALK_IDLE_BAR, false }, + { ACT_DOD_STAND_IDLE, ACT_DOD_STAND_IDLE_BAR, false }, + { ACT_DOD_CROUCH_IDLE, ACT_DOD_CROUCH_IDLE_BAR, false }, + { ACT_DOD_CROUCHWALK_IDLE, ACT_DOD_CROUCHWALK_IDLE_BAR, false }, + { ACT_DOD_WALK_IDLE, ACT_DOD_WALK_IDLE_BAR, false }, + { ACT_DOD_RUN_IDLE, ACT_DOD_RUN_IDLE_BAR, false }, + { ACT_SPRINT, ACT_DOD_SPRINT_IDLE_BAR, false }, + + { ACT_RANGE_ATTACK1, ACT_DOD_PRIMARYATTACK_BAR, false }, + { ACT_DOD_PRIMARYATTACK_CROUCH, ACT_DOD_PRIMARYATTACK_BAR, false }, + { ACT_DOD_PRIMARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_BAR, false }, + + { ACT_RELOAD, ACT_DOD_RELOAD_BAR, false }, + { ACT_DOD_RELOAD_CROUCH, ACT_DOD_RELOAD_CROUCH_BAR, false }, + { ACT_DOD_RELOAD_PRONE, ACT_DOD_RELOAD_PRONE_BAR, false }, + + // Hand Signals + { ACT_DOD_HS_IDLE, ACT_DOD_HS_IDLE_K98, false }, + { ACT_DOD_HS_CROUCH, ACT_DOD_HS_CROUCH_K98, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponBAR );
\ No newline at end of file diff --git a/game/shared/dod/weapon_bazooka.cpp b/game/shared/dod/weapon_bazooka.cpp new file mode 100644 index 0000000..00240cc --- /dev/null +++ b/game/shared/dod/weapon_bazooka.cpp @@ -0,0 +1,104 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodbaserpg.h" + +#if defined( CLIENT_DLL ) + + #include "c_dod_player.h" + #define CWeaponBazooka C_WeaponBazooka + +#else + + #include "rocket_bazooka.h" + #include "dod_player.h" + +#endif + + +class CWeaponBazooka : public CDODBaseRocketWeapon +{ +public: + DECLARE_CLASS( CWeaponBazooka, CDODBaseRocketWeapon ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeaponBazooka() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_BAZOOKA; } + + virtual void FireRocket( void ); + +private: + CWeaponBazooka( const CWeaponBazooka & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponBazooka, DT_WeaponBazooka ) + +BEGIN_NETWORK_TABLE( CWeaponBazooka, DT_WeaponBazooka ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponBazooka ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_bazooka, CWeaponBazooka ); +PRECACHE_WEAPON_REGISTER( weapon_bazooka ); + +acttable_t CWeaponBazooka::m_acttable[] = +{ + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_BAZOOKA, false }, + { ACT_PRONE_FORWARD, ACT_DOD_PRONEWALK_IDLE_BAZOOKA, false }, + { ACT_DOD_STAND_IDLE, ACT_DOD_STAND_IDLE_BAZOOKA, false }, + { ACT_DOD_CROUCH_IDLE, ACT_DOD_CROUCH_IDLE_BAZOOKA, false }, + { ACT_DOD_CROUCHWALK_IDLE, ACT_DOD_CROUCHWALK_IDLE_BAZOOKA, false }, + { ACT_DOD_WALK_IDLE, ACT_DOD_WALK_IDLE_BAZOOKA, false }, + { ACT_DOD_RUN_IDLE, ACT_DOD_RUN_IDLE_BAZOOKA, false }, + { ACT_SPRINT, ACT_DOD_SPRINT_IDLE_BAZOOKA, false }, + + // Zoomed Aim + { ACT_DOD_IDLE_ZOOMED, ACT_DOD_STAND_ZOOM_BAZOOKA, false }, + { ACT_DOD_CROUCH_ZOOMED, ACT_DOD_CROUCH_ZOOM_BAZOOKA, false }, + { ACT_DOD_CROUCHWALK_ZOOMED, ACT_DOD_CROUCHWALK_ZOOM_BAZOOKA, false }, + { ACT_DOD_WALK_ZOOMED, ACT_DOD_WALK_ZOOM_BAZOOKA, false }, + { ACT_DOD_PRONE_ZOOMED, ACT_DOD_PRONE_ZOOM_BAZOOKA, false }, + { ACT_DOD_PRONE_FORWARD_ZOOMED, ACT_DOD_PRONE_ZOOM_FORWARD_BAZOOKA, false }, + + // Attack ( must be zoomed ) + { ACT_RANGE_ATTACK1, ACT_DOD_PRIMARYATTACK_BAZOOKA, false }, + { ACT_DOD_PRIMARYATTACK_CROUCH, ACT_DOD_PRIMARYATTACK_BAZOOKA, false }, + { ACT_DOD_PRIMARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_BAZOOKA, false }, + + // Reload ( zoomed or not, prone or not ) + { ACT_RELOAD, ACT_DOD_RELOAD_BAZOOKA, false }, + { ACT_DOD_RELOAD_CROUCH, ACT_DOD_RELOAD_CROUCH_BAZOOKA, false }, + { ACT_DOD_RELOAD_PRONE, ACT_DOD_RELOAD_PRONE_BAZOOKA, false }, + { ACT_DOD_RELOAD_DEPLOYED, ACT_DOD_ZOOMLOAD_BAZOOKA, false }, + { ACT_DOD_RELOAD_PRONE_DEPLOYED, ACT_DOD_ZOOMLOAD_PRONE_BAZOOKA, false }, + + // Hand Signals + { ACT_DOD_HS_IDLE, ACT_DOD_HS_IDLE_BAZOOKA, false }, + { ACT_DOD_HS_CROUCH, ACT_DOD_HS_CROUCH_BAZOOKA, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponBazooka ); + +void CWeaponBazooka::FireRocket( void ) +{ +#ifndef CLIENT_DLL + + CBasePlayer *pPlayer = GetPlayerOwner(); + +#ifdef DBGFLAG_ASSERT + CBazookaRocket *pRocket = +#endif //DEBUG + CBazookaRocket::Create( pPlayer->Weapon_ShootPosition(), pPlayer->EyeAngles(), pPlayer ); + + Assert( pRocket ); + +#endif +} diff --git a/game/shared/dod/weapon_c96.cpp b/game/shared/dod/weapon_c96.cpp new file mode 100644 index 0000000..c9a0e62 --- /dev/null +++ b/game/shared/dod/weapon_c96.cpp @@ -0,0 +1,131 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodfullauto.h" + + +#if defined( CLIENT_DLL ) + +#define CWeaponC96 C_WeaponC96 + +#endif + + +class CWeaponC96 : public CDODFullAutoWeapon +{ +public: + DECLARE_CLASS( CWeaponC96, CDODFullAutoWeapon ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeaponC96() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_C96; } + + virtual Activity GetIdleActivity( void ); + virtual Activity GetPrimaryAttackActivity( void ); + virtual Activity GetDrawActivity( void ); + virtual Activity GetReloadActivity( void ); + + virtual float GetRecoil( void ) { return 3.0f; } + +private: + CWeaponC96( const CWeaponC96 & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponC96, DT_WeaponC96 ) + +BEGIN_NETWORK_TABLE( CWeaponC96, DT_WeaponC96 ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponC96 ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_c96, CWeaponC96 ); +PRECACHE_WEAPON_REGISTER( weapon_c96 ); + +acttable_t CWeaponC96::m_acttable[] = +{ + { ACT_DOD_STAND_AIM, ACT_DOD_STAND_AIM_C96, false }, + { ACT_DOD_CROUCH_AIM, ACT_DOD_CROUCH_AIM_C96, false }, + { ACT_DOD_CROUCHWALK_AIM, ACT_DOD_CROUCHWALK_AIM_C96, false }, + { ACT_DOD_WALK_AIM, ACT_DOD_WALK_AIM_C96, false }, + { ACT_DOD_RUN_AIM, ACT_DOD_RUN_AIM_C96, false }, + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_C96, false }, + { ACT_PRONE_FORWARD, ACT_DOD_PRONEWALK_IDLE_C96, false }, + { ACT_DOD_STAND_IDLE, ACT_DOD_STAND_IDLE_C96, false }, + { ACT_DOD_CROUCH_IDLE, ACT_DOD_CROUCH_IDLE_C96, false }, + { ACT_DOD_CROUCHWALK_IDLE, ACT_DOD_CROUCHWALK_IDLE_C96, false }, + { ACT_DOD_WALK_IDLE, ACT_DOD_WALK_IDLE_C96, false }, + { ACT_DOD_RUN_IDLE, ACT_DOD_RUN_IDLE_C96, false }, + { ACT_SPRINT, ACT_DOD_SPRINT_IDLE_C96, false }, + + // Attack ( prone? ) + { ACT_RANGE_ATTACK1, ACT_DOD_PRIMARYATTACK_C96, false }, + { ACT_DOD_PRIMARYATTACK_CROUCH, ACT_DOD_PRIMARYATTACK_C96, false }, + { ACT_DOD_PRIMARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_C96,false }, + + // Reload ( prone? ) + { ACT_RELOAD, ACT_DOD_RELOAD_C96, false }, + { ACT_DOD_RELOAD_CROUCH, ACT_DOD_RELOAD_CROUCH_C96, false }, + { ACT_DOD_RELOAD_PRONE, ACT_DOD_RELOAD_PRONE_C96, false }, + + // Hand Signals + { ACT_DOD_HS_IDLE, ACT_DOD_HS_IDLE_MP44, false }, + { ACT_DOD_HS_CROUCH, ACT_DOD_HS_CROUCH_MP44, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponC96 ); + +Activity CWeaponC96::GetIdleActivity( void ) +{ + Activity actIdle; + + if( m_iClip1 <= 0 ) + actIdle = ACT_VM_IDLE_EMPTY; + else + actIdle = ACT_VM_IDLE; + + return actIdle; +} + +Activity CWeaponC96::GetPrimaryAttackActivity( void ) +{ + Activity actPrim; + + if( m_iClip1 <= 0 ) + actPrim = ACT_VM_PRIMARYATTACK_EMPTY; + else + actPrim = ACT_VM_PRIMARYATTACK; + + return actPrim; +} + +Activity CWeaponC96::GetDrawActivity( void ) +{ + Activity actDraw; + + if( m_iClip1 <= 0 ) + actDraw = ACT_VM_DRAW_EMPTY; + else + actDraw = ACT_VM_DRAW; + + return actDraw; +} + +Activity CWeaponC96::GetReloadActivity( void ) +{ + Activity actReload; + + if( m_iClip1 <= 0 ) + actReload = ACT_VM_RELOAD_EMPTY; + else + actReload = ACT_VM_RELOAD; + + return actReload; +}
\ No newline at end of file diff --git a/game/shared/dod/weapon_colt.cpp b/game/shared/dod/weapon_colt.cpp new file mode 100644 index 0000000..3715087 --- /dev/null +++ b/game/shared/dod/weapon_colt.cpp @@ -0,0 +1,128 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodsemiauto.h" + +#if defined( CLIENT_DLL ) + + #define CWeaponColt C_WeaponColt + +#endif + + +class CWeaponColt : public CDODSemiAutoWeapon +{ +public: + DECLARE_CLASS( CWeaponColt, CDODSemiAutoWeapon ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeaponColt() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_COLT; } + + virtual Activity GetIdleActivity( void ); + virtual Activity GetPrimaryAttackActivity( void ); + virtual Activity GetDrawActivity( void ); + virtual Activity GetReloadActivity( void ); + + virtual float GetRecoil( void ) { return 1.4f; } + +private: + CWeaponColt( const CWeaponColt & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponColt, DT_WeaponColt ) + +BEGIN_NETWORK_TABLE( CWeaponColt, DT_WeaponColt ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponColt ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_colt, CWeaponColt ); +PRECACHE_WEAPON_REGISTER( weapon_colt ); + +acttable_t CWeaponColt::m_acttable[] = +{ + { ACT_DOD_STAND_AIM, ACT_DOD_STAND_AIM_PISTOL, false }, + { ACT_DOD_CROUCH_AIM, ACT_DOD_CROUCH_AIM_PISTOL, false }, + { ACT_DOD_CROUCHWALK_AIM, ACT_DOD_CROUCHWALK_AIM_PISTOL, false }, + { ACT_DOD_WALK_AIM, ACT_DOD_WALK_AIM_PISTOL, false }, + { ACT_DOD_RUN_AIM, ACT_DOD_RUN_AIM_PISTOL, false }, + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_PISTOL, false }, + { ACT_PRONE_FORWARD, ACT_DOD_PRONEWALK_IDLE_PISTOL, false }, + { ACT_DOD_STAND_IDLE, ACT_DOD_STAND_IDLE_PISTOL, false }, + { ACT_DOD_CROUCH_IDLE, ACT_DOD_CROUCH_IDLE_PISTOL, false }, + { ACT_DOD_CROUCHWALK_IDLE, ACT_DOD_CROUCHWALK_IDLE_PISTOL, false }, + { ACT_DOD_WALK_IDLE, ACT_DOD_WALK_IDLE_PISTOL, false }, + { ACT_DOD_RUN_IDLE, ACT_DOD_RUN_IDLE_PISTOL, false }, + { ACT_SPRINT, ACT_DOD_SPRINT_IDLE_PISTOL, false }, + + { ACT_RANGE_ATTACK1, ACT_DOD_PRIMARYATTACK_PISTOL, false }, + { ACT_DOD_PRIMARYATTACK_CROUCH, ACT_DOD_PRIMARYATTACK_PISTOL, false }, + { ACT_DOD_PRIMARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_PISTOL, false }, + + { ACT_RELOAD, ACT_DOD_RELOAD_PISTOL, false }, + { ACT_DOD_RELOAD_CROUCH, ACT_DOD_RELOAD_CROUCH_PISTOL, false }, + { ACT_DOD_RELOAD_PRONE, ACT_DOD_RELOAD_PRONE_PISTOL, false }, + + // Hand Signals + { ACT_DOD_HS_IDLE, ACT_DOD_HS_IDLE_PISTOL, false }, + { ACT_DOD_HS_CROUCH, ACT_DOD_HS_CROUCH_PISTOL, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponColt ); + +Activity CWeaponColt::GetIdleActivity( void ) +{ + Activity actIdle; + + if( m_iClip1 <= 0 ) + actIdle = ACT_VM_IDLE_EMPTY; + else + actIdle = ACT_VM_IDLE; + + return actIdle; +} + +Activity CWeaponColt::GetPrimaryAttackActivity( void ) +{ + Activity actPrim; + + if( m_iClip1 <= 0 ) + actPrim = ACT_VM_PRIMARYATTACK_EMPTY; + else + actPrim = ACT_VM_PRIMARYATTACK; + + return actPrim; +} + +Activity CWeaponColt::GetDrawActivity( void ) +{ + Activity actDraw; + + if( m_iClip1 <= 0 ) + actDraw = ACT_VM_DRAW_EMPTY; + else + actDraw = ACT_VM_DRAW; + + return actDraw; +} + +Activity CWeaponColt::GetReloadActivity( void ) +{ + Activity actReload; + + if( m_iClip1 <= 0 ) + actReload = ACT_VM_RELOAD_EMPTY; + else + actReload = ACT_VM_RELOAD; + + return actReload; +}
\ No newline at end of file diff --git a/game/shared/dod/weapon_dodbase.cpp b/game/shared/dod/weapon_dodbase.cpp new file mode 100644 index 0000000..3c1fc98 --- /dev/null +++ b/game/shared/dod/weapon_dodbase.cpp @@ -0,0 +1,1416 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "in_buttons.h" +#include "takedamageinfo.h" +#include "weapon_dodbase.h" +#include "ammodef.h" +#include "dod_gamerules.h" + +#ifdef CLIENT_DLL +extern IVModelInfoClient* modelinfo; +#else +extern IVModelInfo* modelinfo; +#include "ilagcompensationmanager.h" +#endif + + +#if defined( CLIENT_DLL ) + + #include "vgui/ISurface.h" + #include "vgui_controls/Controls.h" + #include "c_dod_player.h" + #include "hud_crosshair.h" + #include "SoundEmitterSystem/isoundemittersystembase.h" + +#else + + #include "dod_player.h" + +#endif + +#include "effect_dispatch_data.h" + + +// ----------------------------------------------------------------------------- // +// Global functions. +// ----------------------------------------------------------------------------- // + +bool IsAmmoType( int iAmmoType, const char *pAmmoName ) +{ + return GetAmmoDef()->Index( pAmmoName ) == iAmmoType; +} + +//-------------------------------------------------------------------------------------------------------- +// +// Given a weapon ID, return its alias +// +const char *WeaponIDToAlias( int id ) +{ + if ( (id >= WEAPON_MAX) || (id < 0) ) + return NULL; + + return s_WeaponAliasInfo[id]; +} + +// ----------------------------------------------------------------------------- // +// CWeaponDODBase tables. +// ----------------------------------------------------------------------------- // + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponDODBase, DT_WeaponDODBase ) + +BEGIN_NETWORK_TABLE( CWeaponDODBase, DT_WeaponDODBase ) +#ifdef CLIENT_DLL + RecvPropInt( RECVINFO(m_iReloadModelIndex) ), + RecvPropVector( RECVINFO( m_vInitialDropVelocity ) ), + RecvPropTime( RECVINFO( m_flSmackTime ) ) +#else + SendPropVector( SENDINFO( m_vInitialDropVelocity ), + 20, // nbits + 0, // flags + -3000, // low value + 3000 // high value + ), + SendPropModelIndex( SENDINFO(m_iReloadModelIndex) ), + SendPropTime( SENDINFO( m_flSmackTime ) ) +#endif +END_NETWORK_TABLE() + +LINK_ENTITY_TO_CLASS( weapon_dod_base, CWeaponDODBase ); + + +#ifdef GAME_DLL + + BEGIN_DATADESC( CWeaponDODBase ) + + DEFINE_FUNCTION( FallThink ), + DEFINE_FUNCTION( Die ), + + DEFINE_FUNCTION( Smack ) + + END_DATADESC() + +#else + BEGIN_PREDICTION_DATA( CWeaponDODBase ) + DEFINE_PRED_FIELD( m_flSmackTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ), // for rifle melee attacks + DEFINE_FIELD( m_bInAttack, FIELD_BOOLEAN ) + END_PREDICTION_DATA() +#endif + +Vector head_hull_mins( -16, -16, -18 ); +Vector head_hull_maxs( 16, 16, 18 ); + +void FindHullIntersection( const Vector &vecSrc, trace_t &tr, const Vector &mins, const Vector &maxs, CBaseEntity *pEntity ) +{ + int i, j, k; + float distance; + Vector minmaxs[2] = {mins, maxs}; + trace_t tmpTrace; + Vector vecHullEnd = tr.endpos; + Vector vecEnd; + + CTraceFilterSimple filter( pEntity, COLLISION_GROUP_NONE ); + + distance = 1e6f; + + vecHullEnd = vecSrc + ((vecHullEnd - vecSrc)*2); + UTIL_TraceLine( vecSrc, vecHullEnd, MASK_SOLID, &filter, &tmpTrace ); + if ( tmpTrace.fraction < 1.0 ) + { + tr = tmpTrace; + return; + } + + for ( i = 0; i < 2; i++ ) + { + for ( j = 0; j < 2; j++ ) + { + for ( k = 0; k < 2; k++ ) + { + vecEnd.x = vecHullEnd.x + minmaxs[i][0]; + vecEnd.y = vecHullEnd.y + minmaxs[j][1]; + vecEnd.z = vecHullEnd.z + minmaxs[k][2]; + + UTIL_TraceLine( vecSrc, vecEnd, MASK_SOLID, &filter, &tmpTrace ); + if ( tmpTrace.fraction < 1.0 ) + { + float thisDistance = (tmpTrace.endpos - vecSrc).Length(); + if ( thisDistance < distance ) + { + tr = tmpTrace; + distance = thisDistance; + } + } + } + } + } +} + +// ----------------------------------------------------------------------------- // +// CWeaponDODBase implementation. +// ----------------------------------------------------------------------------- // +CWeaponDODBase::CWeaponDODBase() +{ + SetPredictionEligible( true ); + m_bInAttack = false; + m_iAltFireHint = 0; + AddSolidFlags( FSOLID_TRIGGER ); // Nothing collides with these but it gets touches. + + m_flNextPrimaryAttack = 0; +} + + +bool CWeaponDODBase::IsPredicted() const +{ + return true; +} + +bool CWeaponDODBase::PlayEmptySound() +{ + CPASAttenuationFilter filter( this ); + filter.UsePredictionRules(); + EmitSound( filter, entindex(), "Default.ClipEmpty_Rifle" ); + + return false; +} + + +CBasePlayer* CWeaponDODBase::GetPlayerOwner() const +{ + return dynamic_cast< CBasePlayer* >( GetOwner() ); +} + +CDODPlayer* CWeaponDODBase::GetDODPlayerOwner() const +{ + return dynamic_cast< CDODPlayer* >( GetOwner() ); +} + +bool CWeaponDODBase::SendWeaponAnim( int iActivity ) +{ + return BaseClass::SendWeaponAnim( iActivity ); +} + +bool CWeaponDODBase::CanAttack( void ) +{ + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + + if ( pPlayer ) + { + return pPlayer->CanAttack(); + } + + return false; +} + +bool CWeaponDODBase::ShouldAutoReload( void ) +{ + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + + if ( pPlayer ) + { + return pPlayer->ShouldAutoReload(); + } + + return false; +} + +void CWeaponDODBase::ItemPostFrame() +{ + if ( m_flSmackTime > 0 && gpGlobals->curtime > m_flSmackTime ) + { + Smack(); + m_flSmackTime = -1; + } + + CBasePlayer *pPlayer = GetPlayerOwner(); + + if ( !pPlayer ) + return; + +#ifdef _DEBUG + CDODGameRules *mp = DODGameRules(); +#endif + + assert( mp ); + + if ((m_bInReload) && (pPlayer->m_flNextAttack <= gpGlobals->curtime)) + { + // complete the reload. + int j = MIN( GetMaxClip1() - m_iClip1, pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) ); + + // Add them to the clip + m_iClip1 += j; + pPlayer->RemoveAmmo( j, m_iPrimaryAmmoType ); + + m_bInReload = false; + + FinishReload(); + } + + if ((pPlayer->m_nButtons & IN_ATTACK2) && (m_flNextSecondaryAttack <= gpGlobals->curtime)) + { + if ( m_iClip2 != -1 && !pPlayer->GetAmmoCount( GetSecondaryAmmoType() ) ) + { + m_bFireOnEmpty = TRUE; + } + + SecondaryAttack(); + + pPlayer->m_nButtons &= ~IN_ATTACK2; + } + else if ((pPlayer->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime ) && !m_bInAttack ) + { + if ( (m_iClip1 == 0/* && pszAmmo1()*/) || (GetMaxClip1() == -1 && !pPlayer->GetAmmoCount( GetPrimaryAmmoType() ) ) ) + { + m_bFireOnEmpty = TRUE; + } + + if( CanAttack() ) + PrimaryAttack(); + } + else if ( pPlayer->m_nButtons & IN_RELOAD && GetMaxClip1() != WEAPON_NOCLIP && !m_bInReload && m_flNextPrimaryAttack < gpGlobals->curtime) + { + // reload when reload is pressed, or if no buttons are down and weapon is empty. + Reload(); + } + else if ( !(pPlayer->m_nButtons & (IN_ATTACK|IN_ATTACK2) ) ) + { + // no fire buttons down + + m_bFireOnEmpty = false; + + m_bInAttack = false; //reset semi-auto + + if ( !IsUseable() && m_flNextPrimaryAttack < gpGlobals->curtime ) + { + // Intentionally blank -- used to switch weapons here + } + else if( ShouldAutoReload() ) + { + // weapon is useable. Reload if empty and weapon has waited as long as it has to after firing + if ( m_iClip1 == 0 && !(GetWeaponFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < gpGlobals->curtime ) + { + Reload(); + return; + } + } + + WeaponIdle( ); + return; + } +} + +void CWeaponDODBase::WeaponIdle() +{ + if (m_flTimeWeaponIdle > gpGlobals->curtime) + return; + + SendWeaponAnim( GetIdleActivity() ); + + m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration(); +} + +Activity CWeaponDODBase::GetIdleActivity( void ) +{ + return ACT_VM_IDLE; +} + +const CDODWeaponInfo &CWeaponDODBase::GetDODWpnData() const +{ + const FileWeaponInfo_t *pWeaponInfo = &GetWpnData(); + const CDODWeaponInfo *pDODInfo; + + #ifdef _DEBUG + pDODInfo = dynamic_cast< const CDODWeaponInfo* >( pWeaponInfo ); + Assert( pDODInfo ); + #else + pDODInfo = static_cast< const CDODWeaponInfo* >( pWeaponInfo ); + #endif + + return *pDODInfo; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *CWeaponDODBase::GetViewModel( int /*viewmodelindex = 0 -- this is ignored in the base class here*/ ) const +{ + if ( GetPlayerOwner() == NULL ) + { + return BaseClass::GetViewModel(); + } + + return GetWpnData().szViewModel; +} + +void CWeaponDODBase::Precache( void ) +{ + // precache base first, it loads weapon scripts + BaseClass::Precache(); + + PrecacheScriptSound( "Default.ClipEmpty_Rifle" ); + + PrecacheParticleSystem( "muzzle_pistols" ); + PrecacheParticleSystem( "muzzle_fullyautomatic" ); + PrecacheParticleSystem( "muzzle_rifles" ); + PrecacheParticleSystem( "muzzle_rockets" ); + PrecacheParticleSystem( "muzzle_mg42" ); + + PrecacheParticleSystem( "view_muzzle_pistols" ); + PrecacheParticleSystem( "view_muzzle_fullyautomatic" ); + PrecacheParticleSystem( "view_muzzle_rifles" ); + PrecacheParticleSystem( "view_muzzle_rockets" ); + PrecacheParticleSystem( "view_muzzle_mg42" ); + + const CDODWeaponInfo &info = GetDODWpnData(); + + int iWpnNameLen = Q_strlen(info.m_szReloadModel); + +#ifdef DEBUG + // Make sure that if we declare an alt weapon, that we have criteria to show it + // and vice-versa + //Assert( ((info.m_iAltWpnCriteria & (ALTWPN_CRITERIA_RELOADING | ALTWPN_CRITERIA_FIRING)) > 0 ) == + // (iWpnNameLen > 0) ); +#endif + + if( iWpnNameLen > 0 ) + m_iReloadModelIndex = CBaseEntity::PrecacheModel( info.m_szReloadModel ); +} + +bool CWeaponDODBase::DefaultDeploy( char *szViewModel, char *szWeaponModel, int iActivity, char *szAnimExt ) +{ + CBasePlayer *pOwner = GetPlayerOwner(); + if ( !pOwner ) + { + return false; + } + + pOwner->SetAnimationExtension( szAnimExt ); + + SetViewModel(); + SendWeaponAnim( iActivity ); + + pOwner->SetNextAttack( gpGlobals->curtime + SequenceDuration() ); + m_flNextPrimaryAttack = MAX( m_flNextPrimaryAttack, gpGlobals->curtime ); + m_flNextSecondaryAttack = gpGlobals->curtime; + + SetWeaponVisible( true ); + SetWeaponModelIndex( szWeaponModel ); + + CBaseViewModel *vm = pOwner->GetViewModel( m_nViewModelIndex ); + + Assert( vm ); + + if( vm ) + { + //set sleeves to proper team + switch( pOwner->GetTeamNumber() ) + { + case TEAM_ALLIES: + vm->m_nSkin = SLEEVE_ALLIES; + break; + case TEAM_AXIS: + vm->m_nSkin = SLEEVE_AXIS; + break; + default: + Assert( !"TEAM_UNASSIGNED or spectator getting a view model assigned" ); + break; + } + } + + return true; +} + +void CWeaponDODBase::SetWeaponModelIndex( const char *pName ) +{ + m_iWorldModelIndex = modelinfo->GetModelIndex( pName ); +} + +bool CWeaponDODBase::CanBeSelected( void ) +{ + if ( !VisibleInWeaponSelection() ) + return false; + + return true; +} + +bool CWeaponDODBase::CanDeploy( void ) +{ + return BaseClass::CanDeploy(); +} + +bool CWeaponDODBase::CanHolster( void ) +{ + return BaseClass::CanHolster(); +} + +void CWeaponDODBase::Drop( const Vector &vecVelocity ) +{ +#ifndef CLIENT_DLL + if ( m_iAltFireHint ) + { + CDODPlayer *pPlayer = GetDODPlayerOwner(); + if ( pPlayer ) + { + pPlayer->StopHintTimer( m_iAltFireHint ); + } + } +#endif + + // cancel any reload in progress + m_bInReload = false; + + m_flSmackTime = -1; + + m_vInitialDropVelocity = vecVelocity; + + BaseClass::Drop( m_vInitialDropVelocity ); +} + +bool CWeaponDODBase::Holster( CBaseCombatWeapon *pSwitchingTo ) +{ +#ifndef CLIENT_DLL + CDODPlayer *pPlayer = GetDODPlayerOwner(); + + if ( pPlayer ) + { + pPlayer->SetFOV( pPlayer, 0 ); // reset the default FOV. + + if ( m_iAltFireHint ) + { + pPlayer->StopHintTimer( m_iAltFireHint ); + } + } +#endif + + m_bInReload = false; + + m_flSmackTime = -1; + + return BaseClass::Holster( pSwitchingTo ); +} + +bool CWeaponDODBase::Deploy() +{ +#ifndef CLIENT_DLL + CDODPlayer *pPlayer = GetDODPlayerOwner(); + + if ( pPlayer ) + { + pPlayer->SetFOV( pPlayer, 0 ); + + if ( m_iAltFireHint ) + { + pPlayer->StartHintTimer( m_iAltFireHint ); + } + } +#endif + + return BaseClass::Deploy(); +} + +#ifdef CLIENT_DLL + + void CWeaponDODBase::PostDataUpdate( DataUpdateType_t updateType ) + { + // We need to do this before the C_BaseAnimating code starts to drive + // clientside animation sequences on this model, which will be using bad sequences for the world model. + int iDesiredModelIndex = 0; + C_BasePlayer *localplayer = C_BasePlayer::GetLocalPlayer(); + if ( localplayer && localplayer == GetOwner() && !C_BasePlayer::ShouldDrawLocalPlayer() ) // FIXME: use localplayer->ShouldDrawThisPlayer() instead. + { + iDesiredModelIndex = m_iViewModelIndex; + } + else + { + iDesiredModelIndex = GetWorldModelIndex(); + + // Our world models never animate + SetSequence( 0 ); + } + + if ( GetModelIndex() != iDesiredModelIndex ) + { + SetModelIndex( iDesiredModelIndex ); + } + + BaseClass::PostDataUpdate( updateType ); + } + + void CWeaponDODBase::OnDataChanged( DataUpdateType_t type ) + { + if ( m_iState == WEAPON_NOT_CARRIED && m_iOldState != WEAPON_NOT_CARRIED ) + { + // we are being notified of the weapon being dropped + // add an interpolation history so the movement is smoother + + // Now stick our initial velocity into the interpolation history + CInterpolatedVar< Vector > &interpolator = GetOriginInterpolator(); + + interpolator.ClearHistory(); + float changeTime = GetLastChangeTime( LATCH_SIMULATION_VAR ); + + // Add a sample 1 second back. + Vector vCurOrigin = GetLocalOrigin() - m_vInitialDropVelocity; + interpolator.AddToHead( changeTime - 1.0, &vCurOrigin, false ); + + // Add the current sample. + vCurOrigin = GetLocalOrigin(); + interpolator.AddToHead( changeTime, &vCurOrigin, false ); + + Vector estVel; + EstimateAbsVelocity( estVel ); + + /*Msg( "estimated velocity ( %.1f %.1f %.1f ) initial velocity ( %.1f %.1f %.1f )\n", + estVel.x, + estVel.y, + estVel.z, + m_vInitialDropVelocity.m_Value.x, + m_vInitialDropVelocity.m_Value.y, + m_vInitialDropVelocity.m_Value.z );*/ + + OnWeaponDropped(); + } + + BaseClass::OnDataChanged( type ); + + if ( GetPredictable() && !ShouldPredict() ) + ShutdownPredictable(); + } + + int CWeaponDODBase::GetWorldModelIndex( void ) + { + if( m_bUseAltWeaponModel && GetOwner() != NULL ) + return m_iReloadModelIndex; + else + return m_iWorldModelIndex; + } + + bool CWeaponDODBase::ShouldPredict() + { + if ( GetOwner() && GetOwner() == C_BasePlayer::GetLocalPlayer() ) + return true; + + return BaseClass::ShouldPredict(); + } + + void CWeaponDODBase::ProcessMuzzleFlashEvent() + { + CDODPlayer *pPlayer = GetDODPlayerOwner(); + if ( pPlayer ) + pPlayer->ProcessMuzzleFlashEvent(); + + BaseClass::ProcessMuzzleFlashEvent(); + } +#else + + + //----------------------------------------------------------------------------- + // Purpose: Get the accuracy derived from weapon and player, and return it + //----------------------------------------------------------------------------- + const Vector& CWeaponDODBase::GetBulletSpread() + { + static Vector cone = VECTOR_CONE_8DEGREES; + return cone; + } + + //----------------------------------------------------------------------------- + // Purpose: + //----------------------------------------------------------------------------- + void CWeaponDODBase::ItemBusyFrame() + { + if( ShouldAutoReload() && !m_bInReload ) + { + // weapon is useable. Reload if empty and weapon has waited as long as it has to after firing + if ( m_iClip1 == 0 && !(GetWeaponFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < gpGlobals->curtime ) + { + Reload(); + } + } + + BaseClass::ItemBusyFrame(); + } + + //----------------------------------------------------------------------------- + // Purpose: Match the anim speed to the weapon speed while crouching + //----------------------------------------------------------------------------- + float CWeaponDODBase::GetDefaultAnimSpeed() + { + return 1.0; + } + + bool CWeaponDODBase::ShouldRemoveOnRoundRestart() + { + if ( GetPlayerOwner() ) + return false; + else + return true; + } + + + //========================================================= + // Materialize - make a CWeaponDODBase visible and tangible + //========================================================= + void CWeaponDODBase::Materialize() + { + if ( IsEffectActive( EF_NODRAW ) ) + { + RemoveEffects( EF_NODRAW ); + DoMuzzleFlash(); + } + + AddSolidFlags( FSOLID_TRIGGER ); + + SetThink (&CWeaponDODBase::SUB_Remove); + SetNextThink( gpGlobals->curtime + 1 ); + } + + //========================================================= + // AttemptToMaterialize - the item is trying to rematerialize, + // should it do so now or wait longer? + //========================================================= + void CWeaponDODBase::AttemptToMaterialize() + { + float time = g_pGameRules->FlWeaponTryRespawn( this ); + + if ( time == 0 ) + { + Materialize(); + return; + } + + SetNextThink( gpGlobals->curtime + time ); + } + + //========================================================= + // CheckRespawn - a player is taking this weapon, should + // it respawn? + //========================================================= + void CWeaponDODBase::CheckRespawn() + { + //GOOSEMAN : Do not respawn weapons! + return; + } + + + //========================================================= + // Respawn- this item is already in the world, but it is + // invisible and intangible. Make it visible and tangible. + //========================================================= + CBaseEntity* CWeaponDODBase::Respawn() + { + // make a copy of this weapon that is invisible and inaccessible to players (no touch function). The weapon spawn/respawn code + // will decide when to make the weapon visible and touchable. + CBaseEntity *pNewWeapon = CBaseEntity::Create( GetClassname(), g_pGameRules->VecWeaponRespawnSpot( this ), GetAbsAngles(), GetOwner() ); + + if ( pNewWeapon ) + { + pNewWeapon->AddEffects( EF_NODRAW );// invisible for now + pNewWeapon->SetTouch( NULL );// no touch + pNewWeapon->SetThink( &CWeaponDODBase::AttemptToMaterialize ); + + UTIL_DropToFloor( this, MASK_SOLID ); + + // not a typo! We want to know when the weapon the player just picked up should respawn! This new entity we created is the replacement, + // but when it should respawn is based on conditions belonging to the weapon that was taken. + pNewWeapon->SetNextThink( gpGlobals->curtime + g_pGameRules->FlWeaponRespawnTime( this ) ); + } + else + { + Msg( "Respawn failed to create %s!\n", GetClassname() ); + } + + return pNewWeapon; + } + + bool CWeaponDODBase::Reload() + { + return BaseClass::Reload(); + } + + void CWeaponDODBase::Spawn() + { + BaseClass::Spawn(); + + // Set this here to allow players to shoot dropped weapons + SetCollisionGroup( COLLISION_GROUP_WEAPON ); + + SetExtraAmmoCount(0); //Start with no additional ammo + + CollisionProp()->UseTriggerBounds( true, 10.0f ); + } + + void CWeaponDODBase::SetDieThink( bool bDie ) + { + if( bDie ) + SetContextThink( &CWeaponDODBase::Die, gpGlobals->curtime + 45.0f, "DieContext" ); + else + SetContextThink( NULL, gpGlobals->curtime, "DieContext" ); + } + + void CWeaponDODBase::Die( void ) + { + UTIL_Remove( this ); + } + +#endif + +void CWeaponDODBase::OnPickedUp( CBaseCombatCharacter *pNewOwner ) +{ + BaseClass::OnPickedUp( pNewOwner ); + +#if !defined( CLIENT_DLL ) + SetDieThink( false ); +#endif +} + +bool CWeaponDODBase::DefaultReload( int iClipSize1, int iClipSize2, int iActivity ) +{ + CBaseCombatCharacter *pOwner = GetOwner(); + if (!pOwner) + return false; + + // If I don't have any spare ammo, I can't reload + if ( pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0 ) + return false; + + bool bReload = false; + + // If you don't have clips, then don't try to reload them. + if ( UsesClipsForAmmo1() ) + { + // need to reload primary clip? + int primary = min(iClipSize1 - m_iClip1, pOwner->GetAmmoCount(m_iPrimaryAmmoType)); + if ( primary != 0 ) + { + bReload = true; + } + } + + if ( UsesClipsForAmmo2() ) + { + // need to reload secondary clip? + int secondary = min(iClipSize2 - m_iClip2, pOwner->GetAmmoCount(m_iSecondaryAmmoType)); + if ( secondary != 0 ) + { + bReload = true; + } + } + + if ( !bReload ) + return false; + + CDODPlayer *pPlayer = GetDODPlayerOwner(); + if ( pPlayer ) + { +#ifdef CLIENT_DLL + PlayWorldReloadSound( pPlayer ); +#else + pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD ); +#endif + } + + SendWeaponAnim( iActivity ); + + // Play the player's reload animation + if ( pOwner->IsPlayer() ) + { + ( ( CBasePlayer * )pOwner)->SetAnimation( PLAYER_RELOAD ); + } + + float flSequenceEndTime = gpGlobals->curtime + SequenceDuration(); + pOwner->SetNextAttack( flSequenceEndTime ); + m_flNextPrimaryAttack = m_flNextSecondaryAttack = flSequenceEndTime; + + m_bInReload = true; + + return true; +} + +#ifdef CLIENT_DLL + void CWeaponDODBase::PlayWorldReloadSound( CDODPlayer *pPlayer ) + { + Assert( pPlayer ); + + const char *shootsound = GetShootSound( RELOAD ); + if ( !shootsound || !shootsound[0] ) + return; + + CSoundParameters params; + + if ( !GetParametersForSound( shootsound, params, NULL ) ) + return; + + // Play weapon sound from the owner + CPASAttenuationFilter filter( pPlayer, params.soundlevel ); + filter.RemoveRecipient( pPlayer ); // no local player, that is done in the model + + EmitSound( filter, pPlayer->entindex(), shootsound, NULL, 0.0 ); + } +#endif + +bool CWeaponDODBase::IsUseable() +{ + CBasePlayer *pPlayer = GetPlayerOwner(); + + if ( Clip1() <= 0 ) + { + if ( pPlayer->GetAmmoCount( GetPrimaryAmmoType() ) <= 0 && GetMaxClip1() != -1 ) + { + // clip is empty (or nonexistant) and the player has no more ammo of this type. + return false; + } + } + + return true; +} + +#ifndef CLIENT_DLL +ConVar dod_meleeattackforcescale( "dod_meleeattackforcescale", "8.0", FCVAR_CHEAT | FCVAR_GAMEDLL ); +#endif + +void CWeaponDODBase::RifleButt( void ) +{ + //MeleeAttack( 60, MELEE_DMG_BUTTSTOCK | MELEE_DMG_SECONDARYATTACK, 0.2f, 0.9f ); +} + +void CWeaponDODBase::Bayonet( void ) +{ + //MeleeAttack( 60, MELEE_DMG_BAYONET | MELEE_DMG_SECONDARYATTACK, 0.2f, 0.9f ); +} + +void CWeaponDODBase::Punch( void ) +{ + MeleeAttack( 60, MELEE_DMG_FIST | MELEE_DMG_SECONDARYATTACK, 0.2f, 0.4f ); +} + +//-------------------------------------------- +// iDamageAmount - how much damage to give +// iDamageType - DMG_ bits +// flDmgDelay - delay between attack and the giving of damage, usually timed to animation +// flAttackDelay - time until we can next attack +//-------------------------------------------- +CBaseEntity *CWeaponDODBase::MeleeAttack( int iDamageAmount, int iDamageType, float flDmgDelay, float flAttackDelay ) +{ + if ( !CanAttack() ) + return NULL; + + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + +#if !defined (CLIENT_DLL) + // Move other players back to history positions based on local player's lag + lagcompensation->StartLagCompensation( pPlayer, pPlayer->GetCurrentCommand() ); +#endif + + Vector vForward, vRight, vUp; + AngleVectors( pPlayer->EyeAngles(), &vForward, &vRight, &vUp ); + Vector vecSrc = pPlayer->Weapon_ShootPosition(); + Vector vecEnd = vecSrc + vForward * 48; + + CTraceFilterSimple filter( pPlayer, COLLISION_GROUP_NONE ); + + int iTraceMask = MASK_SOLID | CONTENTS_HITBOX | CONTENTS_DEBRIS; + + trace_t tr; + UTIL_TraceLine( vecSrc, vecEnd, iTraceMask, &filter, &tr ); + + const float rayExtension = 40.0f; + UTIL_ClipTraceToPlayers( vecSrc, vecEnd + vForward * rayExtension, iTraceMask, &filter, &tr ); + + // If the exact forward trace did not hit, try a larger swept box + if ( tr.fraction >= 1.0 ) + { + Vector head_hull_mins( -16, -16, -18 ); + Vector head_hull_maxs( 16, 16, 18 ); + + UTIL_TraceHull( vecSrc, vecEnd, head_hull_mins, head_hull_maxs, MASK_SOLID, &filter, &tr ); + if ( tr.fraction < 1.0 ) + { + // Calculate the point of intersection of the line (or hull) and the object we hit + // This is and approximation of the "best" intersection + CBaseEntity *pHit = tr.m_pEnt; + if ( !pHit || pHit->IsBSPModel() ) + FindHullIntersection( vecSrc, tr, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX, pPlayer ); + vecEnd = tr.endpos; // This is the point on the actual surface (the hull could have hit space) + + // Make sure it is in front of us + Vector vecToEnd = vecEnd - vecSrc; + VectorNormalize( vecToEnd ); + + // if zero length, always hit + if ( vecToEnd.Length() > 0 ) + { + float dot = DotProduct( vForward, vecToEnd ); + + // sanity that our hit is within range + if ( abs(dot) < 0.95 ) + { + // fake that we actually missed + tr.fraction = 1.0; + } + } + } + } + + WeaponSound( MELEE_MISS ); + + bool bDidHit = ( tr.fraction < 1.0f ); + + if ( bDidHit ) //if the swing hit + { + // delay the decal a bit + m_trHit = tr; + + // Store the ent in an EHANDLE, just in case it goes away by the time we get into our think function. + m_pTraceHitEnt = tr.m_pEnt; + + m_iSmackDamage = iDamageAmount; + m_iSmackDamageType = iDamageType; + + m_flSmackTime = gpGlobals->curtime + flDmgDelay; + } + + SendWeaponAnim( GetMeleeActivity() ); + + // player animation + pPlayer->DoAnimationEvent( PLAYERANIMEVENT_SECONDARY_ATTACK ); + + m_flNextPrimaryAttack = gpGlobals->curtime + flAttackDelay; + m_flNextSecondaryAttack = gpGlobals->curtime + flAttackDelay; + m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration(); + +#ifndef CLIENT_DLL + IGameEvent * event = gameeventmanager->CreateEvent( "dod_stats_weapon_attack" ); + if ( event ) + { + event->SetInt( "attacker", pPlayer->GetUserID() ); + event->SetInt( "weapon", GetAltWeaponID() ); + + gameeventmanager->FireEvent( event ); + } + + lagcompensation->FinishLagCompensation( pPlayer ); +#endif //CLIENT_DLL + + return tr.m_pEnt; +} + +//Think function to delay the impact decal until the animation is finished playing +void CWeaponDODBase::Smack() +{ + Assert( GetPlayerOwner() ); + + if ( !GetPlayerOwner() ) + return; + + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + + if ( !pPlayer ) + return; + + // Check that we are still facing the victim + Vector vForward, vRight, vUp; + AngleVectors( pPlayer->EyeAngles(), &vForward, &vRight, &vUp ); + Vector vecSrc = pPlayer->Weapon_ShootPosition(); + Vector vecEnd = vecSrc + vForward * 48; + + CTraceFilterSimple filter( pPlayer, COLLISION_GROUP_NONE ); + + int iTraceMask = MASK_SOLID | CONTENTS_HITBOX | CONTENTS_DEBRIS; + + trace_t tr; + UTIL_TraceLine( vecSrc, vecEnd, iTraceMask, &filter, &tr ); + + const float rayExtension = 40.0f; + UTIL_ClipTraceToPlayers( vecSrc, vecEnd + vForward * rayExtension, iTraceMask, &filter, &tr ); + + if ( tr.fraction >= 1.0 ) + { + Vector head_hull_mins( -16, -16, -18 ); + Vector head_hull_maxs( 16, 16, 18 ); + + UTIL_TraceHull( vecSrc, vecEnd, head_hull_mins, head_hull_maxs, MASK_SOLID, &filter, &tr ); + if ( tr.fraction < 1.0 ) + { + // Calculate the point of intersection of the line (or hull) and the object we hit + // This is and approximation of the "best" intersection + CBaseEntity *pHit = tr.m_pEnt; + if ( !pHit || pHit->IsBSPModel() ) + FindHullIntersection( vecSrc, tr, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX, pPlayer ); + vecEnd = tr.endpos; // This is the point on the actual surface (the hull could have hit space) + } + } + + m_trHit = tr; + + if ( !m_trHit.m_pEnt || (m_trHit.surface.flags & SURF_SKY) ) + return; + + if ( m_trHit.fraction == 1.0 ) + return; + + CPASAttenuationFilter attenuationFilter( this ); + attenuationFilter.UsePredictionRules(); + + if( m_trHit.m_pEnt->IsPlayer() ) + { + if ( m_iSmackDamageType & MELEE_DMG_STRONGATTACK ) + WeaponSound( SPECIAL1 ); + else + WeaponSound( MELEE_HIT ); + } + else + WeaponSound( MELEE_HIT_WORLD ); + + int iDamageType = DMG_CLUB | DMG_NEVERGIB; + +#ifndef CLIENT_DLL + //if they hit the bounding box, just assume a chest hit + if( m_trHit.hitgroup == HITGROUP_GENERIC ) + m_trHit.hitgroup = HITGROUP_CHEST; + + float flDamage = (float)m_iSmackDamage; + + CTakeDamageInfo info( pPlayer, pPlayer, flDamage, iDamageType ); + + if ( m_iSmackDamageType & MELEE_DMG_SECONDARYATTACK ) + info.SetDamageCustom( MELEE_DMG_SECONDARYATTACK ); + + float flScale = (1.0f / flDamage) * dod_meleeattackforcescale.GetFloat(); + + Vector vecForceDir = vForward; + + CalculateMeleeDamageForce( &info, vecForceDir, m_trHit.endpos, flScale ); + + Assert( m_trHit.m_pEnt != GetPlayerOwner() ); + + m_trHit.m_pEnt->DispatchTraceAttack( info, vForward, &m_trHit ); + ApplyMultiDamage(); +#endif + + // We've gotten minidumps where this happened. + if ( !GetPlayerOwner() ) + return; + + CEffectData data; + data.m_vOrigin = m_trHit.endpos; + data.m_vStart = m_trHit.startpos; + data.m_nSurfaceProp = m_trHit.surface.surfaceProps; + data.m_nHitBox = m_trHit.hitbox; +#ifdef CLIENT_DLL + data.m_hEntity = m_trHit.m_pEnt->GetRefEHandle(); +#else + data.m_nEntIndex = m_trHit.m_pEnt->entindex(); +#endif + + CPASFilter effectfilter( data.m_vOrigin ); + +#ifndef CLIENT_DLL + effectfilter.RemoveRecipient( GetPlayerOwner() ); +#endif + + data.m_vAngles = GetPlayerOwner()->GetAbsAngles(); + data.m_fFlags = 0x1; //IMPACT_NODECAL; + data.m_nDamageType = iDamageType; + + bool bHitPlayer = m_trHit.m_pEnt && m_trHit.m_pEnt->IsPlayer(); + + // don't do any impacts if we hit a teammate and ff is off + if ( bHitPlayer && + m_trHit.m_pEnt->GetTeamNumber() == GetPlayerOwner()->GetTeamNumber() && + !friendlyfire.GetBool() ) + return; + + if ( bHitPlayer ) + { + te->DispatchEffect( effectfilter, 0.0, data.m_vOrigin, "Impact", data ); + } + else if ( m_iSmackDamageType & MELEE_DMG_EDGE ) + { + data.m_nDamageType = DMG_SLASH; + te->DispatchEffect( effectfilter, 0.0, data.m_vOrigin, "KnifeSlash", data ); + } +} + +#if defined( CLIENT_DLL ) + + float g_lateralBob = 0; + float g_verticalBob = 0; + + static ConVar cl_bobcycle( "cl_bobcycle","0.8" ); + static ConVar cl_bob( "cl_bob","0.002" ); + static ConVar cl_bobup( "cl_bobup","0.5" ); + + // Register these cvars if needed for easy tweaking + static ConVar v_iyaw_cycle( "v_iyaw_cycle", "2"/*, FCVAR_UNREGISTERED*/ ); + static ConVar v_iroll_cycle( "v_iroll_cycle", "0.5"/*, FCVAR_UNREGISTERED*/ ); + static ConVar v_ipitch_cycle( "v_ipitch_cycle", "1"/*, FCVAR_UNREGISTERED*/ ); + static ConVar v_iyaw_level( "v_iyaw_level", "0.3"/*, FCVAR_UNREGISTERED*/ ); + static ConVar v_iroll_level( "v_iroll_level", "0.1"/*, FCVAR_UNREGISTERED*/ ); + static ConVar v_ipitch_level( "v_ipitch_level", "0.3"/*, FCVAR_UNREGISTERED*/ ); + + //----------------------------------------------------------------------------- + // Purpose: + // Output : float + //----------------------------------------------------------------------------- + float CWeaponDODBase::CalcViewmodelBob( void ) + { + static float bobtime; + static float lastbobtime; + static float lastspeed; + float cycle; + + CBasePlayer *player = ToBasePlayer( GetOwner() ); + //Assert( player ); + + //NOTENOTE: For now, let this cycle continue when in the air, because it snaps badly without it + + if ( ( !gpGlobals->frametime ) || ( player == NULL ) ) + { + //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!) + return 0.0f;// just use old value + } + + //Find the speed of the player + float speed = player->GetLocalVelocity().Length2D(); + float flmaxSpeedDelta = MAX( 0, (gpGlobals->curtime - lastbobtime) * 320.0f ); + + // don't allow too big speed changes + speed = clamp( speed, lastspeed-flmaxSpeedDelta, lastspeed+flmaxSpeedDelta ); + speed = clamp( speed, -320, 320 ); + + lastspeed = speed; + + //FIXME: This maximum speed value must come from the server. + // MaxSpeed() is not sufficient for dealing with sprinting - jdw + + float bob_offset = RemapVal( speed, 0, 320, 0.0f, 1.0f ); + + bobtime += ( gpGlobals->curtime - lastbobtime ) * bob_offset; + lastbobtime = gpGlobals->curtime; + + //Calculate the vertical bob + cycle = bobtime - (int)(bobtime/cl_bobcycle.GetFloat())*cl_bobcycle.GetFloat(); + cycle /= cl_bobcycle.GetFloat(); + + if ( cycle < cl_bobup.GetFloat() ) + { + cycle = M_PI * cycle / cl_bobup.GetFloat(); + } + else + { + cycle = M_PI + M_PI*(cycle-cl_bobup.GetFloat())/(1.0 - cl_bobup.GetFloat()); + } + + g_verticalBob = speed*0.005f; + g_verticalBob = g_verticalBob*0.3 + g_verticalBob*0.7*sin(cycle); + + g_verticalBob = clamp( g_verticalBob, -7.0f, 4.0f ); + + //Calculate the lateral bob + cycle = bobtime - (int)(bobtime/cl_bobcycle.GetFloat()*2)*cl_bobcycle.GetFloat()*2; + cycle /= cl_bobcycle.GetFloat()*2; + + if ( cycle < cl_bobup.GetFloat() ) + { + cycle = M_PI * cycle / cl_bobup.GetFloat(); + } + else + { + cycle = M_PI + M_PI*(cycle-cl_bobup.GetFloat())/(1.0 - cl_bobup.GetFloat()); + } + + g_lateralBob = speed*0.005f; + g_lateralBob = g_lateralBob*0.3 + g_lateralBob*0.7*sin(cycle); + g_lateralBob = clamp( g_lateralBob, -7.0f, 4.0f ); + + //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!) + return 0.0f; + + } + + //----------------------------------------------------------------------------- + // Purpose: + // Input : &origin - + // &angles - + // viewmodelindex - + //----------------------------------------------------------------------------- + void CWeaponDODBase::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles ) + { + Vector forward, right; + AngleVectors( angles, &forward, &right, NULL ); + + CalcViewmodelBob(); + + // Apply bob, but scaled down to 40% + VectorMA( origin, g_verticalBob * 0.4f, forward, origin ); + + // Z bob a bit more + origin[2] += g_verticalBob * 0.1f; + + // bob the angles + angles[ ROLL ] += g_verticalBob * 0.5f; + angles[ PITCH ] -= g_verticalBob * 0.4f; + + angles[ YAW ] -= g_lateralBob * 0.3f; + + // VectorMA( origin, g_lateralBob * 0.2f, right, origin ); + } + + #include "c_te_effect_dispatch.h" + + #define NUM_MUZZLE_FLASH_TYPES 4 + + bool CWeaponDODBase::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options ) + { + if( event == 5001 ) + { + if ( ShouldDrawMuzzleFlash() ) + { + Assert( GetOwnerEntity() == C_BasePlayer::GetLocalPlayer() ); + + const char *pszMuzzleFlashEffect; + + switch( GetDODWpnData().m_iMuzzleFlashType ) + { + case DOD_MUZZLEFLASH_PISTOL: + pszMuzzleFlashEffect = "view_muzzle_pistols"; + break; + case DOD_MUZZLEFLASH_AUTO: + pszMuzzleFlashEffect = "view_muzzle_fullyautomatic"; + break; + case DOD_MUZZLEFLASH_RIFLE: + pszMuzzleFlashEffect = "view_muzzle_rifles"; + break; + case DOD_MUZZLEFLASH_MG: + pszMuzzleFlashEffect = "view_muzzle_miniguns"; + break; + case DOD_MUZZLEFLASH_ROCKET: + pszMuzzleFlashEffect = "view_muzzle_rockets"; + break; + case DOD_MUZZLEFLASH_MG42: + pszMuzzleFlashEffect = "view_muzzle_mg42"; + break; + default: + pszMuzzleFlashEffect = NULL; + break; + } + + if ( pszMuzzleFlashEffect ) + { + pViewModel->ParticleProp()->Create( pszMuzzleFlashEffect, PATTACH_POINT_FOLLOW, 1 ); + } + } + return true; + } + else if( event == 6002 ) + { + CEffectData data; + data.m_nHitBox = atoi( options ); + data.m_hEntity = GetPlayerOwner() ? GetPlayerOwner()->GetRefEHandle() : INVALID_EHANDLE_INDEX; + pViewModel->GetAttachment( 2, data.m_vOrigin, data.m_vAngles ); + + DispatchEffect( "DOD_EjectBrass", data ); + return true; + } + + return BaseClass::OnFireEvent( pViewModel, origin, angles, event, options ); + } + + bool CWeaponDODBase::ShouldAutoEjectBrass( void ) + { + // Don't eject brass if further than N units from the local player + C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); + if ( !pLocalPlayer ) + return true; + + float flMaxDistSqr = 250; + flMaxDistSqr *= flMaxDistSqr; + + float flDistSqr = pLocalPlayer->EyePosition().DistToSqr( GetAbsOrigin() ); + return ( flDistSqr < flMaxDistSqr ); + } + + bool CWeaponDODBase::GetEjectBrassShellType( void ) + { + return 1; + } + + void CWeaponDODBase::SetUseAltModel( bool bUseAltModel ) + { + m_bUseAltWeaponModel = bUseAltModel; + } + + void CWeaponDODBase::CheckForAltWeapon( int iCurrentState ) + { + int iCriteria = GetDODWpnData().m_iAltWpnCriteria; + + bool bUseAltModel = false; + + if( ( iCriteria & iCurrentState ) != 0 ) + bUseAltModel = true; + + SetUseAltModel( bUseAltModel ); + } + + Vector CWeaponDODBase::GetDesiredViewModelOffset( C_DODPlayer *pOwner ) + { + Vector viewOffset = pOwner->GetViewOffset(); + + float flPercent = ( viewOffset.z - VEC_PRONE_VIEW_SCALED( pOwner ).z ) / ( VEC_VIEW_SCALED( pOwner ).z - VEC_PRONE_VIEW_SCALED( pOwner ).z ); + + return ( flPercent * GetDODWpnData().m_vecViewNormalOffset + + ( 1.0 - flPercent ) * GetDODWpnData().m_vecViewProneOffset ); + } + + bool CWeaponDODBase::ShouldDraw( void ) + { + if ( GetModel() == NULL ) + { + // XXX(johns): Removed, doesn't seem to be the proper spot for this warning given that weapons can call + // ShouldDraw before their properties are filled. + + // C_DODPlayer *pPlayer = C_DODPlayer::GetLocalDODPlayer(); + + // Warning( "BADNESS! Tell Matt that the weapon '%s' tried to draw with a null model ( %d, %d, %s ) \n", + // GetDODWpnData().szClassName, + // m_iWorldModelIndex.Get(), m_iReloadModelIndex.Get(), m_bUseAltWeaponModel ? "alt" : "not alt" ); + + return false; + } + + return BaseClass::ShouldDraw(); + } + +#else + + void CWeaponDODBase::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles ) + { + + } + + float CWeaponDODBase::CalcViewmodelBob( void ) + { + return 0.0f; + } + + void CWeaponDODBase::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) + { + if ( CanDrop() == false ) + return; + + CDODPlayer *pPlayer = ToDODPlayer( pActivator ); + + if ( pPlayer ) + { + pPlayer->PickUpWeapon( this ); + } + } + +#endif + diff --git a/game/shared/dod/weapon_dodbase.h b/game/shared/dod/weapon_dodbase.h new file mode 100644 index 0000000..51cf97e --- /dev/null +++ b/game/shared/dod/weapon_dodbase.h @@ -0,0 +1,403 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef WEAPON_DODBASE_H +#define WEAPON_DODBASE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "dod_playeranimstate.h" +#include "dod_weapon_parse.h" + +#if defined( CLIENT_DLL ) + #define CWeaponDODBase C_WeaponDODBase +#endif + +extern int AliasToWeaponID( const char *alias ); +extern const char *WeaponIDToAlias( int id ); +extern bool IsPrimaryWeapon( int id ); +extern bool IsSecondaryWeapon( int id ); + +class CDODPlayer; + +// These are the names of the ammo types that go in the CAmmoDefs and that the +// weapon script files reference. + +#define DOD_AMMO_SUBMG "DOD_AMMO_SUBMG" +#define DOD_AMMO_ROCKET "DOD_AMMO_ROCKET" +#define DOD_AMMO_COLT "DOD_AMMO_COLT" +#define DOD_AMMO_P38 "DOD_AMMO_P38" +#define DOD_AMMO_C96 "DOD_AMMO_C96" +#define DOD_AMMO_WEBLEY "DOD_AMMO_WEBLEY" +#define DOD_AMMO_GARAND "DOD_AMMO_GARAND" +#define DOD_AMMO_K98 "DOD_AMMO_K98" +#define DOD_AMMO_M1CARBINE "DOD_AMMO_M1CARBINE" +#define DOD_AMMO_ENFIELD "DOD_AMMO_ENFIELD" +#define DOD_AMMO_SPRING "DOD_AMMO_SPRING" +#define DOD_AMMO_FG42 "DOD_AMMO_FG42" +#define DOD_AMMO_BREN "DOD_AMMO_BREN" +#define DOD_AMMO_BAR "DOD_AMMO_BAR" +#define DOD_AMMO_30CAL "DOD_AMMO_30CAL" +#define DOD_AMMO_MG34 "DOD_AMMO_MG34" +#define DOD_AMMO_MG42 "DOD_AMMO_MG42" +#define DOD_AMMO_HANDGRENADE "DOD_AMMO_HANDGRENADE" +#define DOD_AMMO_HANDGRENADE_EX "DOD_AMMO_HANDGRENADE_EX" // the EX is for EXploding! :) +#define DOD_AMMO_STICKGRENADE "DOD_AMMO_STICKGRENADE" +#define DOD_AMMO_STICKGRENADE_EX "DOD_AMMO_STICKGRENADE_EX" +#define DOD_AMMO_SMOKEGRENADE_US "DOD_AMMO_SMOKEGRENADE_US" +#define DOD_AMMO_SMOKEGRENADE_GER "DOD_AMMO_SMOKEGRENADE_GER" +#define DOD_AMMO_SMOKEGRENADE_US_LIVE "DOD_AMMO_SMOKEGRENADE_US_LIVE" +#define DOD_AMMO_SMOKEGRENADE_GER_LIVE "DOD_AMMO_SMOKEGRENADE_GER_LIVE" +#define DOD_AMMO_RIFLEGRENADE_US "DOD_AMMO_RIFLEGRENADE_US" +#define DOD_AMMO_RIFLEGRENADE_GER "DOD_AMMO_RIFLEGRENADE_GER" +#define DOD_AMMO_RIFLEGRENADE_US_LIVE "DOD_AMMO_RIFLEGRENADE_US_LIVE" +#define DOD_AMMO_RIFLEGRENADE_GER_LIVE "DOD_AMMO_RIFLEGRENADE_GER_LIVE" + + + +#define CROSSHAIR_CONTRACT_PIXELS_PER_SECOND 7.0f + +// Given an ammo type (like from a weapon's GetPrimaryAmmoType()), this compares it +// against the ammo name you specify. +// MIKETODO: this should use indexing instead of searching and strcmp()'ing all the time. +bool IsAmmoType( int iAmmoType, const char *pAmmoName ); + + +typedef enum +{ + WEAPON_NONE = 0, + + //Melee + WEAPON_AMERKNIFE, + WEAPON_SPADE, + + //Pistols + WEAPON_COLT, + WEAPON_P38, + WEAPON_C96, + + //Rifles + WEAPON_GARAND, + WEAPON_M1CARBINE, + WEAPON_K98, + + //Sniper Rifles + WEAPON_SPRING, + WEAPON_K98_SCOPED, + + //SMG + WEAPON_THOMPSON, + WEAPON_MP40, + WEAPON_MP44, + WEAPON_BAR, + + //Machine guns + WEAPON_30CAL, + WEAPON_MG42, + + //Rocket weapons + WEAPON_BAZOOKA, + WEAPON_PSCHRECK, + + //Grenades + WEAPON_FRAG_US, + WEAPON_FRAG_GER, + + WEAPON_FRAG_US_LIVE, + WEAPON_FRAG_GER_LIVE, + + WEAPON_SMOKE_US, + WEAPON_SMOKE_GER, + + WEAPON_RIFLEGREN_US, + WEAPON_RIFLEGREN_GER, + + WEAPON_RIFLEGREN_US_LIVE, + WEAPON_RIFLEGREN_GER_LIVE, + + // not actually separate weapons, but defines used in stats recording + // find a better way to do this without polluting the list of actual weapons. + WEAPON_THOMPSON_PUNCH, + WEAPON_MP40_PUNCH, + + WEAPON_GARAND_ZOOMED, + WEAPON_K98_ZOOMED, + WEAPON_SPRING_ZOOMED, + WEAPON_K98_SCOPED_ZOOMED, + + WEAPON_30CAL_UNDEPLOYED, + WEAPON_MG42_UNDEPLOYED, + + WEAPON_BAR_SEMIAUTO, + WEAPON_MP44_SEMIAUTO, + + WEAPON_MAX, // number of weapons weapon index + +} DODWeaponID; + + +//Class Heirarchy for dod weapons + +/* + + CWeaponDODBase + | + | + |--> CWeaponDODBaseMelee + | | + | |--> CWeaponSpade + | |--> CWeaponUSKnife + | + |--> CWeaponDODBaseGrenade + | | + | |--> CWeaponHandgrenade + | |--> CWeaponStickGrenade + | |--> CWeaponSmokeGrenadeUS + | |--> CWeaponSmokeGrenadeGER + | + |--> CWeaponBaseRifleGrenade + | | + | |--> CWeaponRifleGrenadeUS + | |--> CWeaponRifleGrenadeGER + | + |--> CDODBaseRocketWeapon + | | + | |--> CWeaponBazooka + | |--> CWeaponPschreck + | + |--> CWeaponDODBaseGun + | + |--> CDODFullAutoWeapon + | | + | |--> CWeaponC96 + | | + | |--> CDODFullAutoPunchWeapon + | | | + | | |--> CWeaponThompson + | | |--> CWeaponMP40 + | | + | |--> CDODBipodWeapon + | | + | |-> CWeapon30Cal + | |-> CWeaponMG42 + | + |--> CDODFireSelectWeapon + | | + | |--> CWeaponMP44 + | |--> CWeaponBAR + | + | + |--> CDODSemiAutoWeapon + | + |--> CWeaponColt + |--> CWeaponP38 + |--> CWeaponM1Carbine + |--> CDODSniperWeapon + | + |--> CWeaponSpring + |--> CWeaponScopedK98 + |--> CWeaponGarand + |--> CWeaponK98 + +*/ + +void FindHullIntersection( const Vector &vecSrc, trace_t &tr, const Vector &mins, const Vector &maxs, CBaseEntity *pEntity ); + +typedef enum +{ + Primary_Mode = 0, + Secondary_Mode, +} DODWeaponMode; + + +class CWeaponDODBase : public CBaseCombatWeapon +{ +public: + DECLARE_CLASS( CWeaponDODBase, CBaseCombatWeapon ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + + CWeaponDODBase(); + + #ifdef GAME_DLL + DECLARE_DATADESC(); + + virtual void CheckRespawn(); + virtual CBaseEntity* Respawn(); + + virtual const Vector& GetBulletSpread(); + virtual float GetDefaultAnimSpeed(); + + virtual void ItemBusyFrame(); + virtual bool ShouldRemoveOnRoundRestart(); + + void Materialize(); + void AttemptToMaterialize(); + + #else + + void PlayWorldReloadSound( CDODPlayer *pPlayer ); + + #endif + + virtual bool DefaultReload( int iClipSize1, int iClipSize2, int iActivity ); + + virtual bool Holster( CBaseCombatWeapon *pSwitchingTo ); + virtual void Drop( const Vector &vecVelocity ); + virtual void AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles ); + virtual float CalcViewmodelBob( void ); + // All predicted weapons need to implement and return true + virtual bool IsPredicted() const; + + virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() | FCAP_USE_IN_RADIUS; } + + CBasePlayer* GetPlayerOwner() const; + CDODPlayer* GetDODPlayerOwner() const; + + virtual void WeaponIdle( void ); + virtual Activity GetIdleActivity( void ); + + // Get DOD-specific weapon data. + CDODWeaponInfo const &GetDODWpnData() const; + + // Get specific DOD weapon ID (ie: WEAPON_GARAND, etc) + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_NONE; } + virtual DODWeaponID GetStatsWeaponID( void ) { return GetWeaponID(); } + virtual DODWeaponID GetAltWeaponID( void ) const { return WEAPON_NONE; } + + // return true if this weapon is an instance of the given weapon type (ie: "IsA" WEAPON_GLOCK) + bool IsA( DODWeaponID id ) const { return GetWeaponID() == id; } + + // return true if this weapon has a silencer equipped + virtual bool IsSilenced( void ) const { return false; } + + void KickBack( float up_base, float lateral_base, float up_modifier, float lateral_modifier, float up_max, float lateral_max, int direction_change ); + + virtual void SetWeaponModelIndex( const char *pName ); + + virtual bool CanDrop( void ) { return false; } + + virtual bool ShouldDrawCrosshair( void ) { return true; } + virtual bool ShouldDrawViewModel( void ) { return true; } + virtual bool ShouldDrawMuzzleFlash( void ) { return true; } + + virtual float GetWeaponAccuracy( float flPlayerSpeed ) { return 0; } + + virtual bool HideViewModelWhenZoomed( void ) { return false; } + + virtual bool CanAttack( void ); + virtual bool ShouldAutoReload( void ); + + CNetworkVar( int, m_iReloadModelIndex ); + CNetworkVector( m_vInitialDropVelocity ); + + virtual void FinishReload( void ) {} + +public: + #if defined( CLIENT_DLL ) + + virtual void ProcessMuzzleFlashEvent(); + virtual bool ShouldPredict(); + + + virtual void PostDataUpdate( DataUpdateType_t type ); + virtual void OnDataChanged( DataUpdateType_t type ); + + virtual bool OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options ); + + virtual bool ShouldAutoEjectBrass( void ); + virtual bool GetEjectBrassShellType( void ); + + void SetUseAltModel( bool bUseAlt ); + virtual int GetWorldModelIndex( void ); + virtual void CheckForAltWeapon( int iCurrentState ); + + virtual Vector GetDesiredViewModelOffset( C_DODPlayer *pOwner ); + virtual float GetViewModelSwayScale( void ) { return 1.0; } + + virtual void OnWeaponDropped( void ) {} + + virtual bool ShouldDraw( void ); + + float m_flCrosshairDistance; + int m_iAmmoLastCheck; + int m_iAlpha; + int m_iScopeTextureID; + + bool m_bUseAltWeaponModel; //use alternate left handed world model? reset on new sequence + #else + + virtual bool Reload(); + virtual void Spawn(); + + void SetDieThink( bool bDie ); + void Die( void ); + + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + + #endif + + virtual void OnPickedUp( CBaseCombatCharacter *pNewOwner ); + + bool IsUseable(); + virtual bool CanDeploy( void ); + virtual bool CanHolster( void ); + virtual bool SendWeaponAnim( int iActivity ); + virtual void Precache( void ); + virtual bool CanBeSelected( void ); + virtual bool DefaultDeploy( char *szViewModel, char *szWeaponModel, int iActivity, char *szAnimExt ); + virtual bool Deploy(); + bool PlayEmptySound(); + virtual void ItemPostFrame(); + + virtual const char *GetViewModel( int viewmodelindex = 0 ) const; + + bool m_bInAttack; //True after a semi-auto weapon fires - will not fire a second time on the same button press + + void SetExtraAmmoCount( int count ) { m_iExtraPrimaryAmmo = count; } + int GetExtraAmmoCount( void ) { return m_iExtraPrimaryAmmo; } + + virtual const char *GetSecondaryDeathNoticeName( void ) { return "world"; } + + virtual CBaseEntity *MeleeAttack( int iDamageAmount, int iDamageType, float flDmgDelay, float flAttackDelay ); + void EXPORT Smack( void ); + //Secondary Attacks + void RifleButt( void ); + void Bayonet( void ); + void Punch( void ); + + virtual Activity GetMeleeActivity( void ) { return ACT_VM_SECONDARYATTACK; } + virtual Activity GetStrongMeleeActivity( void ) { return ACT_VM_SECONDARYATTACK; } + + virtual float GetRecoil( void ) { return 0.0f; } + +protected: + CNetworkVar( float, m_flSmackTime ); + int m_iSmackDamage; + int m_iSmackDamageType; + EHANDLE m_pTraceHitEnt; + trace_t m_trHit; + + int m_iAltFireHint; + +private: + + void EjectBrassLate(); + + float m_flDecreaseShotsFired; + + CWeaponDODBase( const CWeaponDODBase & ); + + int m_iExtraPrimaryAmmo; + +#ifdef CLIENT_DLL + int m_iCrosshairTexture; +#endif +}; + + +#endif // WEAPON_DODBASE_H diff --git a/game/shared/dod/weapon_dodbasebomb.cpp b/game/shared/dod/weapon_dodbasebomb.cpp new file mode 100644 index 0000000..a6a9786 --- /dev/null +++ b/game/shared/dod/weapon_dodbasebomb.cpp @@ -0,0 +1,375 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodbasebomb.h" + + +#ifdef CLIENT_DLL + #include "c_dod_player.h" +#else + #include "dod_player.h" + #include "dod_bombtarget.h" + #include "collisionutils.h" + #include "in_buttons.h" +#endif + + +IMPLEMENT_NETWORKCLASS_ALIASED( DODBaseBombWeapon, DT_BaseBombWeapon ) + + +BEGIN_NETWORK_TABLE( CDODBaseBombWeapon, DT_BaseBombWeapon ) + +#ifdef CLIENT_DLL + //RecvPropBool( RECVINFO(m_bDeployed) ) +#else + //SendPropBool( SENDINFO(m_bDeployed) ) +#endif + +END_NETWORK_TABLE() + + +BEGIN_PREDICTION_DATA( CDODBaseBombWeapon ) +END_PREDICTION_DATA() + +#ifndef CLIENT_DLL + +BEGIN_DATADESC( CDODBaseBombWeapon ) +END_DATADESC() + +#endif + +LINK_ENTITY_TO_CLASS( weapon_basebomb, CDODBaseBombWeapon ); +PRECACHE_WEAPON_REGISTER( weapon_basebomb ); + +acttable_t CDODBaseBombWeapon::m_acttable[] = +{ + { ACT_PRONE_IDLE, ACT_DOD_PRONEWALK_IDLE_PISTOL, false }, //? + { ACT_PRONE_FORWARD, ACT_DOD_PRONEWALK_IDLE_PISTOL, false }, //? + { ACT_DOD_STAND_IDLE, ACT_DOD_STAND_IDLE_TNT, false }, + { ACT_DOD_CROUCH_IDLE, ACT_DOD_CROUCH_IDLE_TNT, false }, //? + { ACT_DOD_CROUCHWALK_IDLE, ACT_DOD_CROUCHWALK_IDLE_TNT, false }, + { ACT_DOD_WALK_IDLE, ACT_DOD_WALK_IDLE_TNT, false }, + { ACT_DOD_RUN_IDLE, ACT_DOD_RUN_IDLE_TNT, false }, + { ACT_SPRINT, ACT_DOD_SPRINT_IDLE_TNT, false }, + + // Hand Signals + { ACT_DOD_HS_IDLE, ACT_DOD_HS_IDLE_PISTOL, false }, + { ACT_DOD_HS_CROUCH, ACT_DOD_HS_CROUCH_PISTOL, false }, +}; + +IMPLEMENT_ACTTABLE( CDODBaseBombWeapon ); + +CDODBaseBombWeapon::CDODBaseBombWeapon() +{ +} + +void CDODBaseBombWeapon::Spawn( ) +{ + WEAPON_FILE_INFO_HANDLE hWpnInfo = LookupWeaponInfoSlot( GetClassname() ); + + Assert( hWpnInfo != GetInvalidWeaponInfoHandle() ); + + CDODWeaponInfo *pWeaponInfo = dynamic_cast< CDODWeaponInfo* >( GetFileWeaponInfoFromHandle( hWpnInfo ) ); + + Assert( pWeaponInfo && "Failed to get CDODWeaponInfo in weapon spawn" ); + + m_pWeaponInfo = pWeaponInfo; + + SetPlanting( false ); + + BaseClass::Spawn(); +} + +void CDODBaseBombWeapon::Precache() +{ + BaseClass::Precache(); +} + +bool CDODBaseBombWeapon::Deploy( ) +{ +#ifdef CLIENT_DLL + CDODPlayer *pPlayer = GetDODPlayerOwner(); + + if ( pPlayer ) + { + pPlayer->HintMessage( HINT_BOMB_FIRST_SELECT ); + } +#endif + + return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), GetDrawActivity(), (char*)GetAnimPrefix() ); +} + +void CDODBaseBombWeapon::PrimaryAttack() +{ +#ifndef CLIENT_DLL + if ( IsPlanting() ) + { + CDODBombTarget *pTarget = (CDODBombTarget *)m_hBombTarget.Get(); + CBasePlayer *pPlayer = GetPlayerOwner(); + + if ( !pTarget || !pPlayer ) + { + CancelPlanting(); + return; + } + + if ( pTarget->CanPlantHere( GetDODPlayerOwner() ) == false ) + { + // if the target is not active anymore, cancel ( someone planted there already? ) + CancelPlanting(); + } + else if ( ( pTarget->GetAbsOrigin() - pPlayer->WorldSpaceCenter() ).Length() > DOD_BOMB_PLANT_RADIUS ) + { + // if we're too far away, cancel + CancelPlanting(); + } + else if ( IsLookingAtBombTarget( pPlayer, pTarget ) == false || ( pPlayer->GetFlags() & FL_ONGROUND ) == 0 ) + { + // not looking at the target anymore + CancelPlanting(); + } + else if ( gpGlobals->curtime > m_flPlantCompleteTime ) + { + // we finished the plant + CompletePlant(); + } + else + { + m_flNextPlantCheck = gpGlobals->curtime + 0.2; + } + + return; + } + + // find nearby, visible bomb targets + CBaseEntity *pEnt = NULL; + CDODBombTarget *pBestTarget = NULL; + + float flBestDist = FLT_MAX; + + CDODPlayer *pPlayer = GetDODPlayerOwner(); + + if ( !pPlayer ) + return; + + while( ( pEnt = gEntList.FindEntityByClassname( pEnt, "dod_bomb_target" ) ) != NULL ) + { + CDODBombTarget *pTarget = static_cast<CDODBombTarget *>( pEnt ); + + if ( !pTarget->CanPlantHere( pPlayer ) ) + continue; + + Vector pos = pPlayer->WorldSpaceCenter(); + + float flDist = ( pos - pTarget->GetAbsOrigin() ).Length(); + + // if we are looking directly at a bomb target and it is within our radius, that automatically wins + if ( flDist < flBestDist && + flDist < DOD_BOMB_PLANT_RADIUS && + IsLookingAtBombTarget( pPlayer, pTarget ) && + ( pPlayer->GetFlags() & FL_ONGROUND ) ) + { + flBestDist = flDist; + pBestTarget = pTarget; + } + } + + if ( pBestTarget ) + { + StartPlanting( pBestTarget ); + } + + m_flNextPlantCheck = gpGlobals->curtime + 0.2; + + // true if the player is not holding primary attack + m_bUsePlant = !( pPlayer->m_nButtons & (IN_ATTACK|IN_ATTACK2) ); +#endif +} + +void CDODBaseBombWeapon::SecondaryAttack() +{ + PrimaryAttack(); +} + +#ifndef CLIENT_DLL +void CDODBaseBombWeapon::StartPlanting( CDODBombTarget *pTarget ) +{ + // we have already checked that we can plant here + + // store a pointer to the target we're bombing + m_hBombTarget = pTarget; + + // must do this after setting the bomb target as we tell the planter + // what target they are at + SetPlanting( true ); + + // set the timer for when we'll be done + m_flPlantCompleteTime = gpGlobals->curtime + DOD_BOMB_PLANT_TIME; + + // play the planting animation + SendWeaponAnim( ACT_VM_PRIMARYATTACK ); + + CDODPlayer *pPlayer = GetDODPlayerOwner(); + + if ( pPlayer ) + { + pPlayer->DoAnimationEvent( PLAYERANIMEVENT_PLANT_TNT ); + + pPlayer->SetMaxSpeed( 1 ); + + pPlayer->SetProgressBarTime( DOD_BOMB_PLANT_TIME ); + } +} + +bool CDODBaseBombWeapon::CancelPlanting( void ) +{ + bool bHolster = false; + + SetPlanting( false ); + + // play a stop animation + SendWeaponAnim( ACT_VM_IDLE ); + + CDODPlayer *pPlayer = GetDODPlayerOwner(); + + if ( pPlayer ) + { + pPlayer->DoAnimationEvent( PLAYERANIMEVENT_CANCEL_GESTURES ); + + // restore player speed + pPlayer->SetMaxSpeed( 600 ); + + pPlayer->ResetProgressBar(); + + if ( m_bUsePlant ) + { + pPlayer->SelectLastItem(); + + bHolster = true; + } + } + + return bHolster; +} + +void CDODBaseBombWeapon::CompletePlant( void ) +{ + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + + if ( pPlayer ) + { + SetPlanting( false ); + + // restore player speed + GetDODPlayerOwner()->SetMaxSpeed( 600 ); + + // Tell the target that we finished planting the bomb + ((CDODBombTarget *)m_hBombTarget.Get())->CompletePlanting( pPlayer ); + + // destroy the bomb weapon + pPlayer->Weapon_Drop( this, NULL, NULL ); + UTIL_Remove(this); + + pPlayer->ResetProgressBar(); + + pPlayer->SelectLastItem(); + } +} + +bool CDODBaseBombWeapon::IsLookingAtBombTarget( CBasePlayer *pPlayer, CDODBombTarget *pTarget ) +{ + Vector forward; + AngleVectors( pPlayer->EyeAngles(), &forward ); + + Vector toBomb = pTarget->GetAbsOrigin() - pPlayer->EyePosition(); + toBomb.NormalizeInPlace(); + + return ( DotProduct( forward, toBomb ) >= 0.8 ); +} + +#endif + +void CDODBaseBombWeapon::ItemPostFrame() +{ +#ifndef CLIENT_DLL + CBasePlayer *pPlayer = GetPlayerOwner(); + + if ( !pPlayer ) + return; + + if ( pPlayer->m_nButtons & (IN_ATTACK|IN_ATTACK2) ) + { + PrimaryAttack(); + } + // Only use the time check if we are planting with the +use key + // adds a slight lag to breaking the player lock otherwise + else if ( !m_bUsePlant || m_flNextPlantCheck < gpGlobals->curtime ) + { + if ( IsPlanting() ) + { + // reset all planting + bool bHolster = CancelPlanting(); + + // sometimes after canceling we put the weapon away and switch + // to our last weapon. In that case, we don't want to send any more + // anim calls b/c it confuses the client. + if ( bHolster ) + { + // we've put this weapon away, stop everything + return; + } + + // anim now + m_flTimeWeaponIdle = 0; + } + + // idle + if (m_flTimeWeaponIdle > gpGlobals->curtime) + return; + + SendWeaponAnim( GetIdleActivity() ); + + m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration(); + + // if we're not planting, why do we have the bomb out? + // switch to our next best weapon + pPlayer->SelectLastItem(); + } + +#endif // CLIENT_DLL +} + +bool CDODBaseBombWeapon::Holster( CBaseCombatWeapon *pSwitchingTo ) +{ +#ifndef CLIENT_DLL + if ( IsPlanting() ) + CancelPlanting(); +#endif + + return BaseClass::Holster( pSwitchingTo ); +} + +void CDODBaseBombWeapon::SetPlanting( bool bPlanting ) +{ + m_bPlanting = bPlanting; + +#ifndef CLIENT_DLL + CDODPlayer *pPlayer = GetDODPlayerOwner(); + + if ( pPlayer ) + { + pPlayer->SetPlanting( m_bPlanting ? (CDODBombTarget *)m_hBombTarget.Get() : NULL ); + } +#endif +} + +bool CDODBaseBombWeapon::IsPlanting( void ) +{ + return m_bPlanting; +}
\ No newline at end of file diff --git a/game/shared/dod/weapon_dodbasebomb.h b/game/shared/dod/weapon_dodbasebomb.h new file mode 100644 index 0000000..2894b64 --- /dev/null +++ b/game/shared/dod/weapon_dodbasebomb.h @@ -0,0 +1,87 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef WEAPON_DODBASEBOMB_H +#define WEAPON_DODBASEBOMB_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "weapon_dodbase.h" + +#if defined( CLIENT_DLL ) + +#define CDODBaseBombWeapon C_DODBaseBombWeapon + +#else + +#include "dod_bombtarget.h" + +#endif + +//----------------------------------------------------------------------------- +// RPG +//----------------------------------------------------------------------------- +class CDODBaseBombWeapon : public CWeaponDODBase +{ +public: + DECLARE_CLASS( CDODBaseBombWeapon, CWeaponDODBase ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CDODBaseBombWeapon(); + + virtual void Spawn(); + virtual void Precache( void ); + virtual bool Deploy(); + virtual void PrimaryAttack(); + virtual void SecondaryAttack(); + virtual bool Holster( CBaseCombatWeapon *pSwitchingTo ); + + virtual void ItemPostFrame( void ); + + virtual bool CanDrop( void ) { return false; } + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_NONE; } + virtual bool ShouldDrawCrosshair( void ) { return false; } + virtual bool HasPrimaryAmmo() { return true; } + virtual bool CanBeSelected() { return false; } + +#ifndef CLIENT_DLL + void StartPlanting( CDODBombTarget *pTarget ); + bool CancelPlanting( void ); + void CompletePlant( void ); + bool IsLookingAtBombTarget( CBasePlayer *pPlayer, CDODBombTarget *pTarget ); +#endif + + void SetPlanting( bool bPlanting ); + bool IsPlanting( void ); + +protected: + CDODWeaponInfo *m_pWeaponInfo; + +private: + CDODBaseBombWeapon( const CDODBaseBombWeapon & ); + + bool m_bPlanting; + + // pointer to the dod_bomb_target that we're planting at + EHANDLE m_hBombTarget; + + // when the plant will be complete + float m_flPlantCompleteTime; + + bool m_bUsePlant; // player hit +use to start this plant + float m_flNextPlantCheck; + +#ifndef CLIENT_DLL + DECLARE_DATADESC(); +#endif +}; + +#endif // WEAPON_DODBASEBOMB_H
\ No newline at end of file diff --git a/game/shared/dod/weapon_dodbasegrenade.cpp b/game/shared/dod/weapon_dodbasegrenade.cpp new file mode 100644 index 0000000..d04255a --- /dev/null +++ b/game/shared/dod/weapon_dodbasegrenade.cpp @@ -0,0 +1,474 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodbasegrenade.h" +#include "in_buttons.h" +#include "dod_gamerules.h" + + +#if defined( CLIENT_DLL ) + #include "c_dod_player.h" +#else + #include "dod_player.h" +#endif + +extern ConVar dod_bonusround; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponDODBaseGrenade, DT_WeaponDODBaseGrenade ) + +BEGIN_NETWORK_TABLE(CWeaponDODBaseGrenade, DT_WeaponDODBaseGrenade) +#if !defined( CLIENT_DLL ) + SendPropBool( SENDINFO( m_bPinPulled ) ), + SendPropBool( SENDINFO( m_bArmed ) ), +#else + RecvPropBool( RECVINFO( m_bPinPulled ) ), + RecvPropBool( RECVINFO( m_bArmed ) ), +#endif +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponDODBaseGrenade ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_basedodgrenade, CWeaponDODBaseGrenade ); + +acttable_t CWeaponDODBaseGrenade::m_acttable[] = +{ + // Move this out to the specific grenades??? + { ACT_DOD_STAND_AIM, ACT_DOD_STAND_AIM_GREN_FRAG, false }, + { ACT_DOD_CROUCH_AIM, ACT_DOD_CROUCH_AIM_GREN_FRAG, false }, + { ACT_DOD_CROUCHWALK_AIM, ACT_DOD_CROUCHWALK_AIM_GREN_FRAG, false }, + { ACT_DOD_WALK_AIM, ACT_DOD_WALK_AIM_GREN_FRAG, false }, + { ACT_DOD_RUN_AIM, ACT_DOD_RUN_AIM_GREN_FRAG, false }, + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_GREN_FRAG, false }, + { ACT_PRONE_FORWARD, ACT_DOD_PRONEWALK_AIM_GREN_FRAG, false }, + { ACT_DOD_STAND_IDLE, ACT_DOD_STAND_AIM_GREN_FRAG, false }, + { ACT_DOD_CROUCH_IDLE, ACT_DOD_CROUCH_AIM_GREN_FRAG, false }, + { ACT_DOD_CROUCHWALK_IDLE, ACT_DOD_CROUCHWALK_AIM_GREN_FRAG, false }, + { ACT_DOD_WALK_IDLE, ACT_DOD_WALK_AIM_GREN_FRAG, false }, + { ACT_DOD_RUN_IDLE, ACT_DOD_RUN_AIM_GREN_FRAG, false }, + { ACT_SPRINT, ACT_DOD_SPRINT_AIM_GREN_FRAG, false }, + + { ACT_RANGE_ATTACK1, ACT_DOD_PRIMARYATTACK_GREN_FRAG, false }, + { ACT_DOD_PRIMARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_GREN_FRAG, false }, + { ACT_DOD_PRIMARYATTACK_CROUCH, ACT_DOD_PRIMARYATTACK_CROUCH_GREN_FRAG, false }, + + // Hand Signals + { ACT_DOD_HS_IDLE, ACT_DOD_HS_IDLE_STICKGRENADE, false }, + { ACT_DOD_HS_CROUCH, ACT_DOD_HS_CROUCH_STICKGRENADE, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponDODBaseGrenade ); + +CWeaponDODBaseGrenade::CWeaponDODBaseGrenade() +{ + m_bRedraw = false; + m_bPinPulled = false; + SetArmed( false ); + + m_iAltFireHint = HINT_USE_PRIME; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CWeaponDODBaseGrenade::CanHolster( void ) +{ + // can only holster hand grenades when not primed! + return ( m_bPinPulled == false && !IsArmed() ); +} + +#ifdef CLIENT_DLL + + void CWeaponDODBaseGrenade::PrimaryAttack() + { + //nothing on the client + } + +#else + + BEGIN_DATADESC( CWeaponDODBaseGrenade ) + DEFINE_FIELD( m_bRedraw, FIELD_BOOLEAN ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "DetonateTime", InputSetDetonateTime ), + END_DATADESC() + + //----------------------------------------------------------------------------- + // Purpose: + //----------------------------------------------------------------------------- + void CWeaponDODBaseGrenade::Precache() + { + PrecacheScriptSound( "Weapon_Grenade.Throw" ); + + // Precache all the grenade minimap icons. + PrecacheMaterial( "sprites/minimap_icons/minimap_riflegren_ger" ); + PrecacheMaterial( "sprites/minimap_icons/minimap_riflegren_us" ); + PrecacheMaterial( "sprites/minimap_icons/grenade_hltv" ); + PrecacheMaterial( "sprites/minimap_icons/stick_hltv" ); + PrecacheMaterial( "sprites/minimap_icons/minimap_smoke_us" ); + PrecacheMaterial( "sprites/minimap_icons/minimap_smoke_ger" ); + + BaseClass::Precache(); + } + + //----------------------------------------------------------------------------- + // Purpose: + //----------------------------------------------------------------------------- + bool CWeaponDODBaseGrenade::Deploy() + { + m_bRedraw = false; + m_bPinPulled = false; + + return BaseClass::Deploy(); + } + + //----------------------------------------------------------------------------- + // Purpose: + // Output : Returns true on success, false on failure. + //----------------------------------------------------------------------------- + bool CWeaponDODBaseGrenade::Holster( CBaseCombatWeapon *pSwitchingTo ) + { + m_bRedraw = false; + +#ifndef CLIENT_DLL + // If they attempt to switch weapons before the throw animation is done, + // allow it, but kill the weapon if we have to. + + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + + if( pPlayer && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0 ) + { + pPlayer->Weapon_Drop( this, NULL, NULL ); + UTIL_Remove(this); + } +#endif + + return BaseClass::Holster( pSwitchingTo ); + } + + //----------------------------------------------------------------------------- + // Purpose: + // Output : Returns true on success, false on failure. + //----------------------------------------------------------------------------- + bool CWeaponDODBaseGrenade::Reload() + { + if ( ( m_bRedraw ) && ( m_flNextPrimaryAttack <= gpGlobals->curtime ) && ( m_flNextSecondaryAttack <= gpGlobals->curtime ) ) + { + //Redraw the weapon + SendWeaponAnim( GetDrawActivity() ); + + //Update our times + m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); + m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration(); + m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration(); + + //Mark this as done + m_bRedraw = false; + } + + return true; + } + +enum +{ + THROW_THROW = 0, + THROW_PRIME +}; + + //----------------------------------------------------------------------------- + // Purpose: + //----------------------------------------------------------------------------- + void CWeaponDODBaseGrenade::PrimaryAttack() + { + if ( IsArmed() ) // live grenade already? + { + StartThrow( THROW_THROW ); + } + else + { + StartThrow( THROW_PRIME ); + } + } + + void CWeaponDODBaseGrenade::StartThrow( int throwType ) + { + if( GetPlayerOwner()->GetWaterLevel() > 2 ) + { + PlayEmptySound(); + m_flNextPrimaryAttack = gpGlobals->curtime + 1.0; + return; + } + + if ( !m_bPinPulled ) + { + m_bPinPulled = true; + + SendWeaponAnim( GetPrimaryAttackActivity() ); + + // Can't prime an already primed grenade! + if ( !IsArmed() && throwType == THROW_PRIME ) + { + SetArmed( true ); + + m_flDetonateTime = gpGlobals->curtime + GetDetonateTimerLength(); + + // start the hissing noise + } + + m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration(); + m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); + m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration(); + } + } + + //----------------------------------------------------------------------------- + // Purpose: + //----------------------------------------------------------------------------- + void CWeaponDODBaseGrenade::ItemPostFrame() + { + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + if ( !pPlayer ) + return; + + CBaseViewModel *pViewModel = pPlayer->GetViewModel( m_nViewModelIndex ); + if ( !pViewModel ) + return; + + if( IsArmed() && gpGlobals->curtime > m_flDetonateTime ) + { + //Drop it! + DropGrenade(); + m_flNextPrimaryAttack = gpGlobals->curtime + 1.0; + m_flNextSecondaryAttack = gpGlobals->curtime + 1.0; + return; + } + + if ( m_bPinPulled && + !(pPlayer->m_nButtons & IN_ATTACK) && + !(pPlayer->m_nButtons & IN_ATTACK2) // If they let go of the fire button, they want to throw the grenade. + ) + { + pPlayer->DoAnimationEvent( PLAYERANIMEVENT_THROW_GRENADE ); + + ThrowGrenade(false); + + m_bRedraw = true; + m_bPinPulled = false; + SetArmed( false ); + + DecrementAmmo( pPlayer ); + + SendWeaponAnim( ACT_VM_THROW ); + } + else + { + BaseClass::ItemPostFrame(); + + if ( m_bRedraw ) + { + if (pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0) + { + pPlayer->Weapon_Drop( this, NULL, NULL ); + UTIL_Remove(this); + } + else + Reload(); + } + } + } + + + //----------------------------------------------------------------------------- + // Purpose: + // Input : *pOwner - + //----------------------------------------------------------------------------- + void CWeaponDODBaseGrenade::DecrementAmmo( CBaseCombatCharacter *pOwner ) + { + pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType ); + + if (pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0) + { + pOwner->Weapon_Drop( this, NULL, NULL ); + UTIL_Remove(this); + } + } + + void CWeaponDODBaseGrenade::DropGrenade( void ) + { + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + if ( !pPlayer ) + return; + + pPlayer->DoAnimationEvent( PLAYERANIMEVENT_THROW_GRENADE ); + + bool bThrow = true; + + DODRoundState state = DODGameRules()->State_Get(); + + // If the player is holding a nade that is going to explode + // and the round is over, not allowing them to throw it + // just don't emit a grenade. + if ( dod_bonusround.GetBool() ) + { + int team = pPlayer->GetTeamNumber(); + + if ( ( team == TEAM_ALLIES && state == STATE_AXIS_WIN ) || + ( team == TEAM_AXIS && state == STATE_ALLIES_WIN ) ) + { + bThrow = false; + } + } + + if ( bThrow ) + ThrowGrenade(true); + + m_bRedraw = true; + m_bPinPulled = false; + SetArmed( false ); + + DecrementAmmo( pPlayer ); + + SendWeaponAnim( ACT_VM_THROW ); + } + + ConVar dod_grenadespeed( "dod_grenadespeed", "4.2", FCVAR_CHEAT ); + ConVar dod_grenademaxspeed( "dod_grenademaxspeed", "1400", FCVAR_CHEAT ); + ConVar dod_grenademinspeed( "dod_grenademinspeed", "500", FCVAR_CHEAT ); + + //----------------------------------------------------------------------------- + // Purpose: + //----------------------------------------------------------------------------- + void CWeaponDODBaseGrenade::ThrowGrenade( bool bDrop ) + { + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); + if ( !pPlayer ) + { + Assert( false ); + return; + } + + QAngle angThrow = pPlayer->LocalEyeAngles(); + + Vector vForward, vRight, vUp; + + if( angThrow.x > 180 ) + angThrow.x -= 360; + + if (angThrow.x < 0) + angThrow.x = -10 + angThrow.x * ((90 - 10) / 90.0); + else + angThrow.x = -10 + angThrow.x * ((90 + 10) / 90.0); + + AngleVectors( angThrow, &vForward, &vRight, &vUp ); + + Vector eyes = pPlayer->GetAbsOrigin() + pPlayer->GetViewOffset(); + Vector vecSrc = eyes + vForward * 16; + + // Start with the player's velocity as the grenade vel + Vector vecPlayerVel; + pPlayer->GetVelocity( &vecPlayerVel, NULL ); + + // Get player angles, not eye angles! + QAngle angPlayerAngles = pPlayer->GetAbsAngles(); + Vector vecPlayerForward; + AngleVectors( angPlayerAngles, &vecPlayerForward ); + Vector vecThrow = DotProduct( vecPlayerVel, vecPlayerForward ) * vecPlayerForward; + + if( bDrop ) + { + vecThrow = vForward; + } + else // we are throwing the grenade + { + // change the speed depending on the throw angle + // straight down is 0%, straight up is 100%, linear between + float flSpeed = ( 90 - angThrow.x ) * dod_grenadespeed.GetFloat(); + + //Msg( "speed %.f\n", flSpeed ); + + flSpeed = clamp( flSpeed, dod_grenademinspeed.GetFloat(), dod_grenademaxspeed.GetFloat() ); + + vecThrow += vForward * flSpeed; + } + + trace_t tr; + UTIL_TraceLine( eyes, vecSrc, MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr ); + + // don't go into the ground + if( tr.fraction < 1.0 ) + { + vecSrc = tr.endpos; + } + + float flTimeLeft; + + if ( IsArmed() ) + flTimeLeft = MAX( 0, m_flDetonateTime - gpGlobals->curtime ); + else + flTimeLeft = GetDetonateTimerLength(); + + EmitGrenade( vecSrc, vec3_angle, vecThrow, AngularImpulse(600,random->RandomInt(-1200,1200),0), pPlayer, flTimeLeft ); + + pPlayer->EmitSound( "Weapon_Grenade.Throw" ); + +#ifndef CLIENT_DLL + IGameEvent * event = gameeventmanager->CreateEvent( "dod_stats_weapon_attack" ); + if ( event ) + { + event->SetInt( "attacker", pPlayer->GetUserID() ); + event->SetInt( "weapon", GetStatsWeaponID() ); + + gameeventmanager->FireEvent( event ); + } +#endif //CLIENT_DLL + } + + //----------------------------------------------------------------------------- + // Purpose: + //----------------------------------------------------------------------------- + void CWeaponDODBaseGrenade::EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer, float flLifeTime /* = GRENADE_FUSE_LENGTH */ ) + { + Assert( 0 && "CBaseCSGrenade::EmitGrenade should not be called. Make sure to implement this in your subclass!\n" ); + } + + //----------------------------------------------------------------------------- + // Purpose: + //----------------------------------------------------------------------------- + bool CWeaponDODBaseGrenade::AllowsAutoSwitchFrom( void ) const + { + return ( m_bPinPulled == false ); + } + + void CWeaponDODBaseGrenade::InputSetDetonateTime( inputdata_t &inputdata ) + { + SetDetonateTime( inputdata.value.Float() ); + } + +#endif // !CLIENT_DLL + +Activity CWeaponDODBaseGrenade::GetIdleActivity( void ) +{ + if ( IsArmed() ) + return ACT_VM_IDLE_EMPTY; + else + return ACT_VM_IDLE; +} + +Activity CWeaponDODBaseGrenade::GetPrimaryAttackActivity( void ) +{ + if ( IsArmed() ) + return ACT_VM_PRIMARYATTACK_EMPTY; + else + return ACT_VM_PULLPIN; +} + +Activity CWeaponDODBaseGrenade::GetDrawActivity( void ) +{ + if ( IsArmed() ) + return ACT_VM_DRAW_EMPTY; + else + return ACT_VM_DRAW; +} + diff --git a/game/shared/dod/weapon_dodbasegrenade.h b/game/shared/dod/weapon_dodbasegrenade.h new file mode 100644 index 0000000..9662fb3 --- /dev/null +++ b/game/shared/dod/weapon_dodbasegrenade.h @@ -0,0 +1,90 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef WEAPON_DODBASEGRENADE_H +#define WEAPON_DODBASEGRENADE_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "weapon_dodbase.h" +#include "dod_shareddefs.h" + + +#ifdef CLIENT_DLL + + #define CWeaponDODBaseGrenade C_WeaponDODBaseGrenade + +#endif + + +class CWeaponDODBaseGrenade : public CWeaponDODBase +{ +public: + DECLARE_CLASS( CWeaponDODBaseGrenade, CWeaponDODBase ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeaponDODBaseGrenade(); + + virtual void PrimaryAttack(); + virtual bool CanHolster(); + + virtual bool IsArmed( void ) { return m_bArmed == true; } + void SetArmed( bool bArmed ) { m_bArmed = bArmed; } + + virtual Activity GetIdleActivity( void ); + virtual Activity GetPrimaryAttackActivity( void ); + virtual Activity GetDrawActivity( void ); + +#ifdef CLIENT_DLL + +#else + DECLARE_DATADESC(); + + virtual void Precache(); + virtual void ItemPostFrame(); + virtual bool AllowsAutoSwitchFrom( void ) const; + virtual bool Deploy(); + virtual bool Holster( CBaseCombatWeapon *pSwitchingTo ); + virtual bool Reload(); + + void DecrementAmmo( CBaseCombatCharacter *pOwner ); + + void StartThrow( int iThrowType ); + + void DropGrenade( void ); //forces the grenade to be dropped + + virtual void ThrowGrenade( bool bDrop ); + + // Each derived grenade class implements this. + virtual void EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer, float flLifeTime = GRENADE_FUSE_LENGTH ); + + void SetDetonateTime( float flDetonateTime ) + { + m_flDetonateTime = flDetonateTime; + SetArmed( true ); + } + + void InputSetDetonateTime( inputdata_t &inputdata ); + + virtual float GetDetonateTimerLength( void ) { return GRENADE_FUSE_LENGTH; } + +protected: + +#endif + + bool m_bRedraw; // Draw the weapon again after throwing a grenade + CNetworkVar( bool, m_bPinPulled ); // Set to true when the pin has been pulled but the grenade hasn't been thrown yet. + CNetworkVar( bool, m_bArmed ); // is the grenade armed? + float m_flDetonateTime; // what time the grenade will explode ( if > 0 ) + CWeaponDODBaseGrenade( const CWeaponDODBaseGrenade & ) {} +}; + + +#endif // WEAPON_DODBASEGRENADE_H diff --git a/game/shared/dod/weapon_dodbasegun.cpp b/game/shared/dod/weapon_dodbasegun.cpp new file mode 100644 index 0000000..93fc0df --- /dev/null +++ b/game/shared/dod/weapon_dodbasegun.cpp @@ -0,0 +1,252 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodbasegun.h" +#include "fx_dod_shared.h" +//#include "effect_dispatch_data.h" + +#ifdef CLIENT_DLL + #include "c_dod_player.h" + #include "prediction.h" +#else + #include "dod_player.h" + #include "te_effect_dispatch.h" + #include "util.h" + #include "ndebugoverlay.h" +#endif + +#ifndef CLIENT_DLL +//----------------------------------------------------------------------------- +// Purpose: Only send to local player if this weapon is the active weapon +// Input : *pStruct - +// *pVarData - +// *pRecipients - +// objectID - +// Output : void* +//----------------------------------------------------------------------------- +void* SendProxy_SendActiveLocalBaseGunDataTable( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID ) +{ + // Get the weapon entity + CBaseCombatWeapon *pWeapon = (CBaseCombatWeapon*)pVarData; + if ( pWeapon ) + { + // Only send this chunk of data to the player carrying this weapon + CBasePlayer *pPlayer = ToBasePlayer( pWeapon->GetOwner() ); + if ( pPlayer ) + { + pRecipients->SetOnly( pPlayer->GetClientIndex() ); + return (void*)pVarData; + } + } + + return NULL; +} +REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_SendActiveLocalBaseGunDataTable ); +#endif + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponDODBaseGun, DT_WeaponDODBaseGun ) + +BEGIN_NETWORK_TABLE_NOBASE( CWeaponDODBaseGun, DT_LocalActiveWeaponBaseGunData ) +END_NETWORK_TABLE() + +BEGIN_NETWORK_TABLE( CWeaponDODBaseGun, DT_WeaponDODBaseGun ) + #ifdef CLIENT_DLL + RecvPropDataTable("LocalActiveWeaponBaseGunData", 0, 0, &REFERENCE_RECV_TABLE(DT_LocalActiveWeaponBaseGunData)) + #else + SendPropDataTable("LocalActiveWeaponBaseGunData", 0, &REFERENCE_SEND_TABLE(DT_LocalActiveWeaponBaseGunData), SendProxy_SendActiveLocalBaseGunDataTable ) + #endif +END_NETWORK_TABLE() + +#ifdef CLIENT_DLL + + BEGIN_PREDICTION_DATA( CWeaponDODBaseGun ) + END_PREDICTION_DATA() + +#else + + BEGIN_DATADESC( CWeaponDODBaseGun ) + END_DATADESC() + +#endif + +LINK_ENTITY_TO_CLASS( weapon_dod_base_gun, CWeaponDODBaseGun ); + +extern ConVar friendlyfire; + +CWeaponDODBaseGun::CWeaponDODBaseGun() +{ +} + + +void CWeaponDODBaseGun::Spawn() +{ + DODBaseGunSpawn(); + + SetZoomed( false ); +} + +void CWeaponDODBaseGun::Precache() +{ + BaseClass::Precache(); + + // Precache all weapon ejections, since every weapon will appear in the game. + PrecacheModel( "models/shells/shell_small.mdl" ); + PrecacheModel( "models/shells/shell_medium.mdl" ); + PrecacheModel( "models/shells/shell_large.mdl" ); + PrecacheModel( "models/shells/garand_clip.mdl" ); +} + +void CWeaponDODBaseGun::PrimaryAttack() +{ + Assert( m_pWeaponInfo ); + + DODBaseGunFire(); +} + +void CWeaponDODBaseGun::DODBaseGunSpawn( void ) +{ + WEAPON_FILE_INFO_HANDLE hWpnInfo = LookupWeaponInfoSlot( GetClassname() ); + + Assert( hWpnInfo != GetInvalidWeaponInfoHandle() ); + + CDODWeaponInfo *pWeaponInfo = dynamic_cast< CDODWeaponInfo* >( GetFileWeaponInfoFromHandle( hWpnInfo ) ); + + Assert( pWeaponInfo && "Failed to get CDODWeaponInfo in weapon spawn" ); + + m_pWeaponInfo = pWeaponInfo; + + BaseClass::Spawn(); +} + +float CWeaponDODBaseGun::GetWeaponAccuracy( float flPlayerSpeed ) +{ + //snipers and deployable weapons inherit this and override when we need a different accuracy + + float flSpread = m_pWeaponInfo->m_flAccuracy; + + if( flPlayerSpeed > 45 ) + flSpread += m_pWeaponInfo->m_flAccuracyMovePenalty; + + return flSpread; +} + +bool CWeaponDODBaseGun::DODBaseGunFire() +{ + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + + Assert( pPlayer ); + + // Out of ammo? + if ( m_iClip1 <= 0 ) + { + if (m_bFireOnEmpty) + { + PlayEmptySound(); + m_flNextPrimaryAttack = gpGlobals->curtime + 0.2; + } + + return false; + } + + if( pPlayer->GetWaterLevel() > 2 ) + { + PlayEmptySound(); + m_flNextPrimaryAttack = gpGlobals->curtime + 1.0; + return false; + } + + /* decrement before calling PlayPrimaryAttackAnim, so we can play the empty anim if necessary */ + m_iClip1--; + + SendWeaponAnim( GetPrimaryAttackActivity() ); + + // player "shoot" animation + pPlayer->SetAnimation( PLAYER_ATTACK1 ); + + FX_FireBullets( + pPlayer->entindex(), + pPlayer->Weapon_ShootPosition(), + pPlayer->EyeAngles() + pPlayer->GetPunchAngle(), + GetWeaponID(), + Primary_Mode, + CBaseEntity::GetPredictionRandomSeed() & 255, + GetWeaponAccuracy( pPlayer->GetAbsVelocity().Length2D() ) ); + + DoFireEffects(); + +#ifndef CLIENT_DLL + IGameEvent * event = gameeventmanager->CreateEvent( "dod_stats_weapon_attack" ); + if ( event ) + { + event->SetInt( "attacker", pPlayer->GetUserID() ); + event->SetInt( "weapon", GetStatsWeaponID() ); + + gameeventmanager->FireEvent( event ); + } +#endif //CLIENT_DLL + +#ifdef CLIENT_DLL + CDODPlayer *p = ToDODPlayer( GetPlayerOwner() ); + + if ( prediction->IsFirstTimePredicted() ) + p->DoRecoil( GetWeaponID(), GetRecoil() ); +#endif + + m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime + GetFireDelay(); + + m_flTimeWeaponIdle = gpGlobals->curtime + m_pWeaponInfo->m_flTimeToIdleAfterFire; + return true; +} + +float CWeaponDODBaseGun::GetFireDelay( void ) +{ + return m_pWeaponInfo->m_flFireDelay; +} + +void CWeaponDODBaseGun::DoFireEffects() +{ + CBasePlayer *pPlayer = GetPlayerOwner(); + + if ( pPlayer ) + pPlayer->DoMuzzleFlash(); +} + + +bool CWeaponDODBaseGun::Reload() +{ + CBasePlayer *pPlayer = GetPlayerOwner(); + + if (pPlayer->GetAmmoCount( GetPrimaryAmmoType() ) <= 0 && m_iClip1 <= 0 ) + { + CDODPlayer *pDODPlayer = ToDODPlayer( pPlayer ); + pDODPlayer->HintMessage( HINT_AMMO_EXHAUSTED ); + return false; + } + + int iResult = DefaultReload( GetMaxClip1(), GetMaxClip2(), GetReloadActivity() ); + if ( !iResult ) + return false; + + pPlayer->SetAnimation( PLAYER_RELOAD ); + + return true; +} + +Activity CWeaponDODBaseGun::GetPrimaryAttackActivity( void ) +{ + return ACT_VM_PRIMARYATTACK; +} + +Activity CWeaponDODBaseGun::GetReloadActivity( void ) +{ + return ACT_VM_RELOAD; +} + +Activity CWeaponDODBaseGun::GetDrawActivity( void ) +{ + return ACT_VM_DRAW; +} diff --git a/game/shared/dod/weapon_dodbasegun.h b/game/shared/dod/weapon_dodbasegun.h new file mode 100644 index 0000000..8ff215d --- /dev/null +++ b/game/shared/dod/weapon_dodbasegun.h @@ -0,0 +1,83 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef WEAPON_DODBASE_GUN_H +#define WEAPON_DODBASE_GUN_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "weapon_dodbase.h" + +// This is the base class for pistols and rifles. +#if defined( CLIENT_DLL ) + + //#include "particles_simple.h" + //#include "particles_localspace.h" + #include "fx.h" + #include "fx_dod_muzzleflash.h" + + #define CWeaponDODBaseGun C_WeaponDODBaseGun + +#else +#endif + +class CWeaponDODBaseGun : public CWeaponDODBase +{ +public: + + DECLARE_CLASS( CWeaponDODBaseGun, CWeaponDODBase ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + + CWeaponDODBaseGun(); + + virtual void Spawn(); + virtual void Precache(); + virtual void PrimaryAttack(); + virtual bool Reload(); + + // Derived classes must call this from inside their Spawn() function instead of + // just chaining the Spawn() call down. + void DODBaseGunSpawn( void ); + + // Derived classes call this to fire a bullet. + bool DODBaseGunFire( void ); + + // Usually plays the shot sound. Guns with silencers can play different sounds. + virtual void DoFireEffects(); + virtual float GetFireDelay( void ); + + virtual float GetWeaponAccuracy( float flPlayerSpeed ); + + //Pure animation calls - inheriting classes can override with specific + //logic, eg if the idle changes depending on the gun being empty or not + virtual Activity GetPrimaryAttackActivity( void ); + virtual Activity GetReloadActivity( void ); + virtual Activity GetDrawActivity( void ); + + virtual bool CanDrop( void ) { return m_pWeaponInfo->m_bCanDrop; } + + void SetZoomed( bool bZoomed ) { m_bZoomed = bZoomed; } + bool IsSniperZoomed( void ) { return m_bZoomed; } + CNetworkVar( bool, m_bZoomed ); + +protected: + CDODWeaponInfo *m_pWeaponInfo; + +private: + CWeaponDODBaseGun( const CWeaponDODBaseGun & ); + +#ifndef CLIENT_DLL + + DECLARE_DATADESC(); + +#endif +}; + + +#endif // WEAPON_DODBASE_GUN_H diff --git a/game/shared/dod/weapon_dodbasemelee.cpp b/game/shared/dod/weapon_dodbasemelee.cpp new file mode 100644 index 0000000..066bbfc --- /dev/null +++ b/game/shared/dod/weapon_dodbasemelee.cpp @@ -0,0 +1,237 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodbasemelee.h" +#include "dod_gamerules.h" + +#if defined( CLIENT_DLL ) + #include "c_dod_player.h" +#else + #include "dod_player.h" + #include "ilagcompensationmanager.h" +#endif + +#include "effect_dispatch_data.h" + + +#define KNIFE_BODYHIT_VOLUME 128 +#define KNIFE_WALLHIT_VOLUME 512 + +// ----------------------------------------------------------------------------- // +// CWeaponDODBaseMelee tables. +// ----------------------------------------------------------------------------- // + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponDODBaseMelee, DT_WeaponDODBaseMelee ) + +BEGIN_NETWORK_TABLE_NOBASE( CWeaponDODBaseMelee, DT_LocalActiveWeaponBaseMeleeData ) +END_NETWORK_TABLE() + +BEGIN_NETWORK_TABLE( CWeaponDODBaseMelee, DT_WeaponDODBaseMelee ) +END_NETWORK_TABLE() + +#ifdef CLIENT_DLL +BEGIN_PREDICTION_DATA( CWeaponDODBaseMelee ) + DEFINE_PRED_FIELD( m_flSmackTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ), +END_PREDICTION_DATA() +#endif + +LINK_ENTITY_TO_CLASS( weapon_dod_base_melee, CWeaponDODBaseMelee ); + +#ifndef CLIENT_DLL + + BEGIN_DATADESC( CWeaponDODBaseMelee ) + DEFINE_FUNCTION( Smack ) + END_DATADESC() + +#endif + +// ----------------------------------------------------------------------------- // +// CWeaponDODBaseMelee implementation. +// ----------------------------------------------------------------------------- // + +CWeaponDODBaseMelee::CWeaponDODBaseMelee() +{ +} + +void CWeaponDODBaseMelee::Spawn() +{ + Precache(); + + WEAPON_FILE_INFO_HANDLE hWpnInfo = LookupWeaponInfoSlot( GetClassname() ); + + Assert( hWpnInfo != GetInvalidWeaponInfoHandle() ); + + CDODWeaponInfo *pWeaponInfo = dynamic_cast< CDODWeaponInfo* >( GetFileWeaponInfoFromHandle( hWpnInfo ) ); + + Assert( pWeaponInfo && "Failed to get CDODWeaponInfo in melee weapon spawn" ); + + m_pWeaponInfo = pWeaponInfo; + + Assert( m_pWeaponInfo ); + + m_iClip1 = -1; + BaseClass::Spawn(); +} + +void CWeaponDODBaseMelee::WeaponIdle( void ) +{ + if ( m_flTimeWeaponIdle > gpGlobals->curtime ) + return; + + SendWeaponAnim( ACT_VM_IDLE ); + + m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration(); +} + +void CWeaponDODBaseMelee::PrimaryAttack() +{ + MeleeAttack( 60, MELEE_DMG_EDGE, 0.2f, 0.4f ); +} + +CBaseEntity *CWeaponDODBaseMelee::MeleeAttack( int iDamageAmount, int iDamageType, float flDmgDelay, float flAttackDelay ) +{ + if ( !CanAttack() ) + return NULL; + + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + +#if !defined (CLIENT_DLL) + // Move other players back to history positions based on local player's lag + lagcompensation->StartLagCompensation( pPlayer, pPlayer->GetCurrentCommand() ); +#endif + + Vector vForward, vRight, vUp; + AngleVectors( pPlayer->EyeAngles(), &vForward, &vRight, &vUp ); + Vector vecSrc = pPlayer->Weapon_ShootPosition(); + Vector vecEnd = vecSrc + vForward * 48; + + CTraceFilterSimple filter( pPlayer, COLLISION_GROUP_NONE ); + + int iTraceMask = MASK_SOLID | CONTENTS_HITBOX | CONTENTS_DEBRIS; + + trace_t tr; + UTIL_TraceLine( vecSrc, vecEnd, iTraceMask, &filter, &tr ); + + const float rayExtension = 40.0f; + UTIL_ClipTraceToPlayers( vecSrc, vecEnd + vForward * rayExtension, iTraceMask, &filter, &tr ); + + if ( tr.fraction >= 1.0 ) + { + Vector head_hull_mins( -16, -16, -18 ); + Vector head_hull_maxs( 16, 16, 18 ); + + UTIL_TraceHull( vecSrc, vecEnd, head_hull_mins, head_hull_maxs, MASK_SOLID, &filter, &tr ); + if ( tr.fraction < 1.0 ) + { + // Calculate the point of intersection of the line (or hull) and the object we hit + // This is and approximation of the "best" intersection + CBaseEntity *pHit = tr.m_pEnt; + if ( !pHit || pHit->IsBSPModel() ) + FindHullIntersection( vecSrc, tr, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX, pPlayer ); + vecEnd = tr.endpos; // This is the point on the actual surface (the hull could have hit space) + + // Make sure it is in front of us + Vector vecToEnd = vecEnd - vecSrc; + VectorNormalize( vecToEnd ); + + // if zero length, always hit + if ( vecToEnd.Length() > 0 ) + { + float dot = DotProduct( vForward, vecToEnd ); + + // sanity that our hit is within range + if ( abs(dot) < 0.95 ) + { + // fake that we actually missed + tr.fraction = 1.0; + } + } + } + } + + bool bDidHit = ( tr.fraction < 1.0f ); + + bool bDoStrongAttack = false; + + if ( bDidHit && tr.m_pEnt->IsPlayer() && tr.m_pEnt->m_takedamage != DAMAGE_YES ) + { + bDidHit = 0; // still play the animation, we just dont attempt to damage this player + } + + if ( bDidHit ) //if the swing hit + { + // delay the decal a bit + m_trHit = tr; + + // Store the ent in an EHANDLE, just in case it goes away by the time we get into our think function. + m_pTraceHitEnt = tr.m_pEnt; + + m_iSmackDamage = iDamageAmount; + m_iSmackDamageType = iDamageType; + + m_flSmackTime = gpGlobals->curtime + flDmgDelay; + + int iOwnerTeam = pPlayer->GetTeamNumber(); + int iVictimTeam = tr.m_pEnt->GetTeamNumber(); + + // do the mega attack if its a player, and we would do damage + if ( tr.m_pEnt->IsPlayer() && + tr.m_pEnt->m_takedamage == DAMAGE_YES && + ( iVictimTeam != iOwnerTeam || ( iVictimTeam == iOwnerTeam && friendlyfire.GetBool() ) ) ) + { + CDODPlayer *pVictim = ToDODPlayer( tr.m_pEnt ); + + Vector victimForward; + AngleVectors( pVictim->GetAbsAngles(), &victimForward ); + + if ( DotProduct( victimForward, vForward ) > 0.3 ) + { + bDoStrongAttack = true; + } + } + } + + if ( bDoStrongAttack ) + { + m_iSmackDamage = 300; + flAttackDelay = 0.9f; + m_flSmackTime = gpGlobals->curtime + 0.4f; + + m_iSmackDamageType = MELEE_DMG_EDGE | MELEE_DMG_STRONGATTACK; + + // play a "Strong" attack + SendWeaponAnim( ACT_VM_SECONDARYATTACK ); + } + else + { + WeaponSound( MELEE_MISS ); + SendWeaponAnim( GetMeleeActivity() ); + } + + // player animation + pPlayer->DoAnimationEvent( PLAYERANIMEVENT_SECONDARY_ATTACK ); + + m_flNextPrimaryAttack = gpGlobals->curtime + flAttackDelay; + m_flNextSecondaryAttack = gpGlobals->curtime + flAttackDelay; + m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration(); + + +#ifndef CLIENT_DLL + IGameEvent * event = gameeventmanager->CreateEvent( "dod_stats_weapon_attack" ); + if ( event ) + { + event->SetInt( "attacker", pPlayer->GetUserID() ); + event->SetInt( "weapon", GetStatsWeaponID() ); + + gameeventmanager->FireEvent( event ); + } + + lagcompensation->FinishLagCompensation( pPlayer ); +#endif //CLIENT_DLL + + return tr.m_pEnt; +}
\ No newline at end of file diff --git a/game/shared/dod/weapon_dodbasemelee.h b/game/shared/dod/weapon_dodbasemelee.h new file mode 100644 index 0000000..325f9f7 --- /dev/null +++ b/game/shared/dod/weapon_dodbasemelee.h @@ -0,0 +1,57 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef WEAPON_DODBASE_MELEE_H +#define WEAPON_DODBASE_MELEE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "weapon_dodbase.h" + +#if defined( CLIENT_DLL ) + #define CWeaponDODBaseMelee C_WeaponDODBaseMelee +#endif + +// ----------------------------------------------------------------------------- // +// class definition. +// ----------------------------------------------------------------------------- // + +class CWeaponDODBaseMelee : public CWeaponDODBase +{ +public: + DECLARE_CLASS( CWeaponDODBaseMelee, CWeaponDODBase ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + + #ifndef CLIENT_DLL + DECLARE_DATADESC(); + #endif + + CWeaponDODBaseMelee(); + + virtual void Spawn(); + virtual void PrimaryAttack(); + virtual void WeaponIdle(); + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_NONE; } + virtual bool ShouldDrawCrosshair( void ) { return false; } + virtual bool HasPrimaryAmmo() { return true; } + virtual bool CanBeSelected() { return true; } + + virtual CBaseEntity *MeleeAttack( int iDamageAmount, int iDamageType, float flDmgDelay, float flAttackDelay ); + + //virtual const char *GetSecondaryDeathNoticeName( void ) { return "stab"; } + +public: + CDODWeaponInfo *m_pWeaponInfo; + +private: + CWeaponDODBaseMelee( const CWeaponDODBaseMelee & ) {} +}; + + +#endif // WEAPON_DODBASE_MELEE_H diff --git a/game/shared/dod/weapon_dodbaserpg.cpp b/game/shared/dod/weapon_dodbaserpg.cpp new file mode 100644 index 0000000..a9d196d --- /dev/null +++ b/game/shared/dod/weapon_dodbaserpg.cpp @@ -0,0 +1,345 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "cbase.h" +#include "dod_gamerules.h" +#include "weapon_dodbaserpg.h" + + +#ifdef CLIENT_DLL + + #include "c_dod_player.h" + #include "prediction.h" + +#else + + #include "dod_player.h" + +#endif + +IMPLEMENT_NETWORKCLASS_ALIASED( DODBaseRocketWeapon, DT_BaseRocketWeapon ) + +BEGIN_NETWORK_TABLE( CDODBaseRocketWeapon, DT_BaseRocketWeapon ) + +#ifdef CLIENT_DLL + RecvPropBool( RECVINFO(m_bDeployed) ) +#else + SendPropBool( SENDINFO(m_bDeployed) ) +#endif + +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CDODBaseRocketWeapon ) +END_PREDICTION_DATA() + +#ifndef CLIENT_DLL + +BEGIN_DATADESC( CDODBaseRocketWeapon ) +END_DATADESC() + +#endif + +LINK_ENTITY_TO_CLASS( weapon_dodbaserpg, CDODBaseRocketWeapon ); + +CDODBaseRocketWeapon::CDODBaseRocketWeapon() +{ +} + +void CDODBaseRocketWeapon::Precache() +{ + BaseClass::Precache(); +} + +bool CDODBaseRocketWeapon::Reload( void ) +{ + CDODPlayer *pPlayer = GetDODPlayerOwner(); + + if (pPlayer->GetAmmoCount( GetPrimaryAmmoType() ) <= 0) + { + CDODPlayer *pDODPlayer = ToDODPlayer( pPlayer ); + pDODPlayer->HintMessage( HINT_AMMO_EXHAUSTED ); + return false; + } + + Activity actReload; + + if( IsDeployed() ) + actReload = ACT_VM_RELOAD_DEPLOYED; + else + actReload = ACT_VM_RELOAD; + + int iResult = DefaultReload( GetMaxClip1(), GetMaxClip2(), actReload ); + if ( !iResult ) + return false; + + pPlayer->SetAnimation( PLAYER_RELOAD ); + + // if we don't want the auto-rezoom, undeploy here + if ( !pPlayer->ShouldAutoRezoom() ) + { + m_bDeployed = false; + pPlayer->SetBazookaDeployed( m_bDeployed ); + } + + return true; +} + +bool CDODBaseRocketWeapon::ShouldPlayerBeSlow( void ) +{ + if( IsDeployed() && !m_bInReload ) + { + return true; + } + else + return false; +} + +void CDODBaseRocketWeapon::Spawn( ) +{ + WEAPON_FILE_INFO_HANDLE hWpnInfo = LookupWeaponInfoSlot( GetClassname() ); + + Assert( hWpnInfo != GetInvalidWeaponInfoHandle() ); + + CDODWeaponInfo *pWeaponInfo = dynamic_cast< CDODWeaponInfo* >( GetFileWeaponInfoFromHandle( hWpnInfo ) ); + + Assert( pWeaponInfo && "Failed to get CDODWeaponInfo in weapon spawn" ); + + m_pWeaponInfo = pWeaponInfo; + + BaseClass::Spawn(); +} + +void CDODBaseRocketWeapon::Drop( const Vector &vecVelocity ) +{ + SetDeployed( false ); + + BaseClass::Drop( vecVelocity ); +} + +bool CDODBaseRocketWeapon::Deploy( ) +{ + return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), GetDrawActivity(), (char*)GetAnimPrefix() ); +} + +Activity CDODBaseRocketWeapon::GetDrawActivity( void ) +{ + return ACT_VM_DRAW; +} + +bool CDODBaseRocketWeapon::CanHolster( void ) +{ + // can't holster if we are delpoyed and not reloading + if ( IsDeployed() && !m_bInReload ) + return false; + + return true; +} + +bool CDODBaseRocketWeapon::Holster( CBaseCombatWeapon *pSwitchingTo ) +{ +#ifndef CLIENT_DLL + + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + pPlayer->SetBazookaDeployed( false ); + +#endif + + SetDeployed( false ); + + return BaseClass::Holster(pSwitchingTo); +} + + +void CDODBaseRocketWeapon::PrimaryAttack() +{ + Assert( m_pWeaponInfo ); + + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + + // Out of ammo? + if ( m_iClip1 <= 0 ) + { + if (m_bFireOnEmpty) + { + PlayEmptySound(); + m_flNextPrimaryAttack = gpGlobals->curtime + 0.2; + } + + return; + } + + if( pPlayer->GetWaterLevel() > 2 ) + { + PlayEmptySound(); + m_flNextPrimaryAttack = gpGlobals->curtime + 1.0; + return; + } + + if( IsDeployed() ) + { + // player "shoot" animation + pPlayer->SetAnimation( PLAYER_ATTACK1 ); + + SendWeaponAnim( ACT_VM_PRIMARYATTACK ); + + FireRocket(); + + DoFireEffects(); + + m_iClip1--; + +#ifdef CLIENT_DLL + if ( prediction->IsFirstTimePredicted() ) + pPlayer->DoRecoil( GetWeaponID(), GetRecoil() ); +#endif + + if ( m_iClip1 <= 0 && pPlayer->GetAmmoCount( GetPrimaryAmmoType() ) <= 0 ) + { + Lower(); + } + + m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration() + 0.5; + m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration() + 0.5; //length of the fire anim! + +#ifndef CLIENT_DLL + IGameEvent * event = gameeventmanager->CreateEvent( "dod_stats_weapon_attack" ); + if ( event ) + { + event->SetInt( "attacker", pPlayer->GetUserID() ); + event->SetInt( "weapon", GetStatsWeaponID() ); + + gameeventmanager->FireEvent( event ); + } +#endif //CLIENT_DLL + } + else + { +#ifdef CLIENT_DLL + pPlayer->HintMessage( HINT_SHOULDER_WEAPON, true ); +#endif + + m_flNextPrimaryAttack = gpGlobals->curtime + 2.0f; + } +} + +void CDODBaseRocketWeapon::FireRocket( void ) +{ + Assert( !"Derived classes must implement this." ); +} + +void CDODBaseRocketWeapon::DoFireEffects() +{ + CBasePlayer *pPlayer = GetPlayerOwner(); + + if ( pPlayer ) + pPlayer->DoMuzzleFlash(); + + //smoke etc +} + +void CDODBaseRocketWeapon::SecondaryAttack() +{ + CBasePlayer *pPlayer = GetPlayerOwner(); + + //if we're underwater, lower it + if( pPlayer->GetWaterLevel() > 2 ) + { + if( IsDeployed() ) + Lower(); + return; + } + + if( IsDeployed() ) + { + Lower(); + } + else + { + if ( CanAttack() ) + Raise(); + } +} + +void CDODBaseRocketWeapon::WeaponIdle() +{ + if (m_flTimeWeaponIdle > gpGlobals->curtime) + return; + + SendWeaponAnim( GetIdleActivity() ); + + m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration(); +} + +Activity CDODBaseRocketWeapon::GetIdleActivity( void ) +{ + Activity actIdle; + + if( IsDeployed() ) + actIdle = ACT_VM_IDLE_DEPLOYED; + else + actIdle = ACT_VM_IDLE; + + return actIdle; +} + +/* Raise the Bazooka to your shoulder */ +void CDODBaseRocketWeapon::Raise() +{ + //raise to the shoulder + SendWeaponAnim( GetRaiseActivity() ); + + m_bDeployed = true; + + m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); + m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration(); + m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration(); +} + +/* Lower the bazooka to running position */ +bool CDODBaseRocketWeapon::Lower() +{ + SendWeaponAnim( GetLowerActivity() ); + + m_bDeployed = false; + + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + pPlayer->SetBazookaDeployed( m_bDeployed ); + + m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); + m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration(); + m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration(); + + return true; +} + +Activity CDODBaseRocketWeapon::GetLowerActivity( void ) +{ + return ACT_VM_UNDEPLOY; +} + +Activity CDODBaseRocketWeapon::GetRaiseActivity( void ) +{ + return ACT_VM_DEPLOY; +} + +#ifdef CLIENT_DLL + +ConVar deployed_bazooka_sensitivity( "deployed_bazooka_sensitivity", "0.6", FCVAR_CHEAT, "Mouse sensitivity while deploying a bazooka" ); + +void CDODBaseRocketWeapon::OverrideMouseInput( float *x, float *y ) +{ + if( m_bDeployed ) + { + float flSensitivity = deployed_bazooka_sensitivity.GetFloat(); + + *x *= flSensitivity; + *y *= flSensitivity; + } +} + +#endif
\ No newline at end of file diff --git a/game/shared/dod/weapon_dodbaserpg.h b/game/shared/dod/weapon_dodbaserpg.h new file mode 100644 index 0000000..0b570c7 --- /dev/null +++ b/game/shared/dod/weapon_dodbaserpg.h @@ -0,0 +1,88 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef WEAPON_DODBASERPG_H +#define WEAPON_DODBASERPG_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "weapon_dodbase.h" + + +#if defined( CLIENT_DLL ) + + #define CDODBaseRocketWeapon C_DODBaseRocketWeapon + +#endif + +//----------------------------------------------------------------------------- +// RPG +//----------------------------------------------------------------------------- +class CDODBaseRocketWeapon : public CWeaponDODBase +{ +public: + DECLARE_CLASS( CDODBaseRocketWeapon, CWeaponDODBase ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + + CDODBaseRocketWeapon(); + + virtual void Spawn(); + virtual void PrimaryAttack(); + virtual void SecondaryAttack(); + virtual bool Deploy(); + virtual bool CanHolster(); + virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); + virtual bool Reload(); + virtual void WeaponIdle(); + virtual void Drop( const Vector &vecVelocity ); + + virtual bool CanDrop( void ) { return ( IsDeployed() == false ); } + + void DoFireEffects(); + + void Precache( void ); + + void Raise(); + bool Lower(); + + virtual Activity GetDrawActivity( void ); + virtual Activity GetIdleActivity( void ); + virtual Activity GetLowerActivity( void ); + virtual Activity GetRaiseActivity( void ); + + virtual void FireRocket( void ); + + inline bool IsDeployed() { return m_bDeployed; } + inline void SetDeployed( bool bDeployed ) { m_bDeployed = bDeployed; } + + bool ShouldPlayerBeSlow( void ); + + virtual bool ShouldAutoEjectBrass( void ) { return false; } + +#ifdef CLIENT_DLL + virtual void OverrideMouseInput( float *x, float *y ); +#endif + + virtual float GetRecoil( void ) { return 10.0f; } + +protected: + CNetworkVar( bool, m_bDeployed ); + + CDODWeaponInfo *m_pWeaponInfo; + +private: + CDODBaseRocketWeapon( const CDODBaseRocketWeapon & ); + +#ifndef CLIENT_DLL + DECLARE_DATADESC(); +#endif +}; + +#endif // WEAPON_DODBASERPG_H
\ No newline at end of file diff --git a/game/shared/dod/weapon_dodbipodgun.cpp b/game/shared/dod/weapon_dodbipodgun.cpp new file mode 100644 index 0000000..09b0d40 --- /dev/null +++ b/game/shared/dod/weapon_dodbipodgun.cpp @@ -0,0 +1,782 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "fx_dod_shared.h" +#include "weapon_dodbipodgun.h" +#include "dod_gamerules.h" +#include "engine/IEngineSound.h" + +#ifndef CLIENT_DLL +#include "ndebugoverlay.h" +#endif + +IMPLEMENT_NETWORKCLASS_ALIASED( DODBipodWeapon, DT_BipodWeapon ) + +BEGIN_NETWORK_TABLE( CDODBipodWeapon, DT_BipodWeapon ) +#ifdef CLIENT_DLL + RecvPropBool( RECVINFO( m_bDeployed ) ), + RecvPropInt( RECVINFO( m_iDeployedReloadModelIndex) ), +#else + SendPropBool( SENDINFO( m_bDeployed ) ), + SendPropModelIndex( SENDINFO(m_iDeployedReloadModelIndex) ), +#endif +END_NETWORK_TABLE() + +#ifdef CLIENT_DLL +BEGIN_PREDICTION_DATA( CDODBipodWeapon ) + DEFINE_PRED_FIELD( m_bDeployed, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ) +END_PREDICTION_DATA() +#endif + +CDODBipodWeapon::CDODBipodWeapon() +{ +} + +void CDODBipodWeapon::Spawn() +{ + SetDeployed( false ); + m_flNextDeployCheckTime = 0; + + m_iCurrentWorldModel = 0; + + m_iAltFireHint = HINT_USE_DEPLOY; + + BaseClass::Spawn(); +} + +void CDODBipodWeapon::SetDeployed( bool bDeployed ) +{ + if ( bDeployed == false ) + { + m_hDeployedOnEnt = NULL; + m_DeployedEntOrigin = vec3_origin; + m_flDeployedHeight = 0; + +#ifdef GAME_DLL + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + if ( pPlayer ) + { + pPlayer->HandleDeployedMGKillCount( 0 ); // reset when we undeploy + } +#endif + } + + m_bDeployed = bDeployed; +} + +void CDODBipodWeapon::Precache( void ) +{ + // precache base first, it loads weapon scripts + BaseClass::Precache(); + + const CDODWeaponInfo &info = GetDODWpnData(); + + if( Q_strlen(info.m_szDeployedModel) > 0 ) + { + Assert( info.m_iAltWpnCriteria & ALTWPN_CRITERIA_DEPLOYED ); + m_iDeployedModelIndex = CBaseEntity::PrecacheModel( info.m_szDeployedModel ); + } + + if( Q_strlen(info.m_szDeployedReloadModel) > 0 ) + { + Assert( info.m_iAltWpnCriteria & ALTWPN_CRITERIA_DEPLOYED_RELOAD ); + m_iDeployedReloadModelIndex = CBaseEntity::PrecacheModel( info.m_szDeployedReloadModel ); + } + + if( Q_strlen(info.m_szProneDeployedReloadModel) > 0 ) + { + Assert( info.m_iAltWpnCriteria & ALTWPN_CRITERIA_PRONE_DEPLOYED_RELOAD ); + m_iProneDeployedReloadModelIndex = CBaseEntity::PrecacheModel( info.m_szProneDeployedReloadModel ); + } + + m_iCurrentWorldModel = m_iWorldModelIndex; + Assert( m_iCurrentWorldModel != 0 ); +} + +bool CDODBipodWeapon::CanDrop( void ) +{ + return ( IsDeployed() == false ); +} + +bool CDODBipodWeapon::CanHolster( void ) +{ + return ( IsDeployed() == false ); +} + +void CDODBipodWeapon::Drop( const Vector &vecVelocity ) +{ + // If a player is killed while deployed, this resets the weapon state + SetDeployed( false ); + + BaseClass::Drop( vecVelocity ); +} + +void CDODBipodWeapon::SecondaryAttack( void ) +{ + // Toggle deployed / undeployed + + if ( IsDeployed() ) + UndeployBipod(); + else + { + if ( CanAttack() ) + { + bool bSuccess = AttemptToDeploy(); + + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + Assert( pPlayer ); + + if ( !bSuccess ) + { + pPlayer->HintMessage( HINT_MG_DEPLOY_USAGE ); + } + else + { +#ifndef CLIENT_DLL + pPlayer->RemoveHintTimer( m_iAltFireHint ); +#endif + } + } + } +} + +bool CDODBipodWeapon::Reload( void ) +{ + bool bSuccess = BaseClass::Reload(); + + if ( bSuccess ) + { + m_flNextSecondaryAttack = gpGlobals->curtime; + } + + return bSuccess; +} + +#include "in_buttons.h" +// check in busy frame too, to catch cancelling reloads +void CDODBipodWeapon::ItemBusyFrame( void ) +{ + BipodThink(); + + CBasePlayer *pPlayer = GetPlayerOwner(); + + if ( !pPlayer ) + return; + + if ((pPlayer->m_nButtons & IN_ATTACK2) && (m_flNextSecondaryAttack <= gpGlobals->curtime)) + { + SecondaryAttack(); + + pPlayer->m_nButtons &= ~IN_ATTACK2; + } + + BaseClass::ItemBusyFrame(); +} + +void CDODBipodWeapon::ItemPostFrame( void ) +{ + BipodThink(); + BaseClass::ItemPostFrame(); +} + +// see if we're still deployed on the same entity at the same height +// in future can be expanded to check when deploying on other ents that may move / die / break +void CDODBipodWeapon::BipodThink( void ) +{ + if ( m_flNextDeployCheckTime < gpGlobals->curtime ) + { + if ( IsDeployed() ) + { + if ( CheckDeployEnt() == false ) + { + UndeployBipod(); + + // cancel any reload in progress + m_bInReload = false; + m_flNextPrimaryAttack = gpGlobals->curtime + 0.1; + m_flNextSecondaryAttack = gpGlobals->curtime + 0.1; + } + } + + m_flNextDeployCheckTime = gpGlobals->curtime + 0.2; + } +} + +void CDODBipodWeapon::DoFireEffects() +{ + BaseClass::DoFireEffects(); + + CBaseEntity *pDeployedOn = m_hDeployedOnEnt.Get(); + + // in future can be expanded to check when deploying on other ents that may move / die / break + if ( pDeployedOn && pDeployedOn->IsPlayer() && IsDeployed() ) + { +#ifndef CLIENT_DLL + CSingleUserRecipientFilter user( (CBasePlayer *)pDeployedOn ); + enginesound->SetPlayerDSP( user, 32, false ); +#endif + } +} + +// Do the work of deploying the gun at the current location and angles +void CDODBipodWeapon::DeployBipod( float flHeight, CBaseEntity *pDeployedOn, float flYawLimitLeft, float flYawLimitRight ) +{ + m_flDeployedHeight = flHeight; + m_hDeployedOnEnt = pDeployedOn; + + if ( pDeployedOn ) + m_DeployedEntOrigin = pDeployedOn->GetAbsOrigin(); + else + m_DeployedEntOrigin = vec3_origin; // world ent + + SendWeaponAnim( GetDeployActivity() ); + SetDeployed( true ); + + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + pPlayer->m_Shared.SetDeployed( true, flHeight ); + pPlayer->m_Shared.SetDeployedYawLimits( flYawLimitLeft, flYawLimitRight ); + + // Save this off so we do duck checks later, even though we won't be flagged as ducking + m_bDuckedWhenDeployed = pPlayer->m_Shared.IsDucking(); + + // More TODO: + // recalc our yaw limits if the item we're deployed on has moved or rotated + // if our new limits are outside our current eye angles, undeploy us + + m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); + m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration(); + m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration(); +} + +// Do the work of undeploying the gun +void CDODBipodWeapon::UndeployBipod( void ) +{ + SendWeaponAnim( GetUndeployActivity() ); + SetDeployed( false ); + + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + pPlayer->m_Shared.SetDeployed( false ); + + // if we cancelled our reload by undeploying, don't let the reload complete + if ( m_bInReload ) + m_bInReload = false; + + m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); + m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration(); + pPlayer->m_flNextAttack = m_flNextPrimaryAttack; + m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration(); +} + +#ifndef CLIENT_DLL +ConVar dod_debugmgdeploy( "dod_debugmgdeploy", "0", FCVAR_CHEAT|FCVAR_GAMEDLL ); +#endif + +bool CDODBipodWeapon::AttemptToDeploy( void ) +{ + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + + if ( pPlayer->GetGroundEntity() == NULL ) + return false; + + if ( pPlayer->m_Shared.IsGettingUpFromProne() || pPlayer->m_Shared.IsGoingProne() ) + return false; + + CBaseEntity *pDeployedOn = NULL; + float flDeployedHeight = 0.0f; + float flYawLimitLeft = 0; + float flYawLimitRight = 0; + + if ( TestDeploy( &flDeployedHeight, &pDeployedOn, &flYawLimitLeft, &flYawLimitRight ) ) + { + if ( pPlayer->m_Shared.IsProne() && !pPlayer->m_Shared.IsGettingUpFromProne() ) + { + DeployBipod( flDeployedHeight, NULL, flYawLimitLeft, flYawLimitRight ); + return true; + } + else + { + float flMinDeployHeight = 24.0; + if( flDeployedHeight >= flMinDeployHeight ) + { + DeployBipod( flDeployedHeight, pDeployedOn, flYawLimitLeft, flYawLimitRight ); + return true; + } + } + } + + return false; +} + +bool CDODBipodWeapon::CheckDeployEnt( void ) +{ + CBaseEntity *pDeployedOn = NULL; + float flDeployedHeight = 0.0f; + + if ( TestDeploy( &flDeployedHeight, &pDeployedOn ) == false ) + return false; + + // If the entity we were deployed on has changed, or has moved, the origin + // of it will be different. If so, recalc our yaw limits. + if ( pDeployedOn ) + { + if ( m_DeployedEntOrigin != pDeployedOn->GetAbsOrigin() ) + { + float flYawLimitLeft = 0, flYawLimitRight = 0; + TestDeploy( &flDeployedHeight, &pDeployedOn, &flYawLimitLeft, &flYawLimitRight ); + + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + + if ( pPlayer ) + pPlayer->m_Shared.SetDeployedYawLimits( flYawLimitLeft, flYawLimitRight ); + + m_DeployedEntOrigin = pDeployedOn->GetAbsOrigin(); + } + } + + // 20 unit tolerance in height + if ( abs( m_flDeployedHeight - flDeployedHeight ) > 20 ) + return false; + + return true; +} + +bool CDODBipodWeapon::TestDeploy( float *flDeployedHeight, CBaseEntity **pDeployedOn, float *flYawLimitLeft /* = NULL */, float *flYawLimitRight /* = NULL */ ) +{ + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + + QAngle angles = pPlayer->EyeAngles(); + + float flPitch = angles[PITCH]; + if( flPitch > 180 ) + { + flPitch -= 360; + } + + if( flPitch > MIN_DEPLOY_PITCH || flPitch < MAX_DEPLOY_PITCH ) + { + return false; + } + + bool bSuccess = false; + + // if we're not finding the range, test at the current angles + if ( flYawLimitLeft == NULL && flYawLimitRight == NULL ) + { + // test our current angle only + bSuccess = TestDeployAngle( pPlayer, flDeployedHeight, pDeployedOn, angles ); + } + else + { + float flSaveYaw = angles[YAW]; + + const float flAngleDelta = 5; + const float flMaxYaw = 45; + + float flLeft = 0; + float flRight = 0; + + float flTestDeployHeight = 0; + CBaseEntity *pTestDeployedOn = NULL; + + // Sweep Left + while ( flLeft <= flMaxYaw ) + { + angles[YAW] = flSaveYaw + flLeft; + + if ( TestDeployAngle( pPlayer, &flTestDeployHeight, &pTestDeployedOn, angles ) == true ) + { + if ( flLeft == 0 ) // first sweep is authoritative on deploy height and entity + { + *flDeployedHeight = flTestDeployHeight; + *pDeployedOn = pTestDeployedOn; + } + else if ( abs( *flDeployedHeight - flTestDeployHeight ) > 20 ) + { + // don't allow yaw to a position that is too different in height + break; + } + + *flYawLimitLeft = flLeft; + } + else + { + break; + } + flLeft += flAngleDelta; + } + + // can't deploy here, drop out early + if ( flLeft <= 0 ) + return false; + + // we already tested directly ahead and it was clear. skip one test + flRight += flAngleDelta; + + // Sweep Right + while ( flRight <= flMaxYaw ) + { + angles[YAW] = flSaveYaw - flRight; + + if ( TestDeployAngle( pPlayer, &flTestDeployHeight, &pTestDeployedOn, angles ) == true ) + { + if ( abs( *flDeployedHeight - flTestDeployHeight ) > 20 ) + { + // don't allow yaw to a position that is too different in height + break; + } + + *flYawLimitRight = flRight; + } + else + { + break; + } + flRight += flAngleDelta; + } + + bSuccess = true; + } + + return bSuccess; +} + +//ConVar dod_deploy_box_size( "dod_deploy_box_size", "8", FCVAR_REPLICATED ); + +#include "util_shared.h" + +// trace filter that ignores all players except the passed one +class CTraceFilterIgnorePlayersExceptFor : public CTraceFilterSimple +{ +public: + // It does have a base, but we'll never network anything below here.. + DECLARE_CLASS( CTraceFilterIgnorePlayersExceptFor, CTraceFilterSimple ); + + CTraceFilterIgnorePlayersExceptFor( const IHandleEntity *passentity, int collisionGroup ) + : CTraceFilterSimple( passentity, collisionGroup ) + { + } + + virtual bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask ) + { + CBaseEntity *pEntity = EntityFromEntityHandle( pServerEntity ); + + if ( pEntity->IsPlayer() ) + { + if ( pEntity != GetPassEntity() ) + { + return false; + } + else + return true; + } + + return true; + } +}; + +#define DEPLOY_DOWNTRACE_FORWARD_DIST 16 +#define DEPLOY_DOWNTRACE_OFFSET 16 // yay for magic numbers + +bool CDODBipodWeapon::TestDeployAngle( CDODPlayer *pPlayer, float *flDeployedHeight, CBaseEntity **pDeployedOn, QAngle angles ) +{ + // make sure we are deployed on the same entity at the same height + trace_t tr; + + angles[PITCH] = 0; + + Vector forward, right, up; + AngleVectors( angles, &forward, &right, &up ); + + // start at top of player bbox + Vector vecStart = pPlayer->GetAbsOrigin(); + + float flForwardTraceDist = 32; + + // check us as ducking if we are ducked, or if were ducked when we were deployed + bool bDucking = pPlayer->m_Shared.IsDucking() || ( IsDeployed() && m_bDuckedWhenDeployed ); + + if ( pPlayer->m_Shared.IsProne() ) + { + vecStart.z += VEC_PRONE_HULL_MAX[2]; + flForwardTraceDist = 16; + } + else if ( bDucking ) + { + vecStart.z += VEC_DUCK_HULL_MAX[2]; + } + else + { + vecStart.z += 60; + } + + int dim = 1; // dod_deploy_box_size.GetInt(); + Vector vecDeployTraceBoxSize( dim, dim, dim ); + + vecStart.z -= vecDeployTraceBoxSize[2]; + vecStart.z -= 4; + + // sandbags are around 50 units high. Shouldn't be able to deploy on anything a lot higher than that + + // optimal standing height ( for animation's sake ) is around 42 units + // optimal ducking height is around 20 units ( 20 unit high object, plus 8 units of gun ) + + // Start one half box width away from the edge of the player hull + Vector vecForwardStart = vecStart + forward * ( VEC_HULL_MAX_SCALED( pPlayer )[0] + vecDeployTraceBoxSize[0] ); + + int traceMask = MASK_SOLID; + + CBaseEntity *pDeployedOnPlayer = NULL; + + if ( m_hDeployedOnEnt && m_hDeployedOnEnt->IsPlayer() ) + { + pDeployedOnPlayer = m_hDeployedOnEnt.Get(); + } + + CTraceFilterIgnorePlayersExceptFor deployedFilter( pDeployedOnPlayer, COLLISION_GROUP_NONE ); + CTraceFilterSimple undeployedFilter( pPlayer, COLLISION_GROUP_NONE ); + + // if we're deployed, skip all players except for the deployed on player + // if we're not, only skip ourselves + ITraceFilter *filter; + if ( IsDeployed() ) + filter = &deployedFilter; + else + filter = &undeployedFilter; + + UTIL_TraceHull( vecForwardStart, + vecForwardStart + forward * ( flForwardTraceDist - 2 * vecDeployTraceBoxSize[0] ), + -vecDeployTraceBoxSize, + vecDeployTraceBoxSize, + traceMask, + filter, + &tr ); + +#ifndef CLIENT_DLL + if ( dod_debugmgdeploy.GetBool() ) + { + NDebugOverlay::Line( vecForwardStart, vecForwardStart + forward * ( flForwardTraceDist - 2 * vecDeployTraceBoxSize[0] ), 0, 0, 255, true, 0.1 ); + NDebugOverlay::Box( vecForwardStart, -vecDeployTraceBoxSize, vecDeployTraceBoxSize, 255, 0, 0, 128, 0.1 ); + NDebugOverlay::Box( tr.endpos, -vecDeployTraceBoxSize, vecDeployTraceBoxSize, 0, 0, 255, 128, 0.1 ); + } +#endif + + // Test forward, are we trying to deploy into a solid object? + if ( tr.fraction < 1.0 ) + { + return false; + } + + // If we're prone, we can always deploy, don't do the ground test + if ( pPlayer->m_Shared.IsProne() && !pPlayer->m_Shared.IsGettingUpFromProne() ) + { + // MATTTODO: do trace from *front* of player, not from the edge of crouch hull + // this is sufficient + *flDeployedHeight = PRONE_DEPLOY_HEIGHT; + return true; + } + + // fix prediction hitch when coming up from prone. client thinks we aren't + // prone, but hull is still prone hull + // assumes prone hull is shorter than duck hull! + if ( pPlayer->WorldAlignMaxs().z <= VEC_PRONE_HULL_MAX.z ) + return false; + + // Else trace down + Vector vecDownTraceStart = vecStart + forward * ( VEC_HULL_MAX_SCALED( pPlayer )[0] + DEPLOY_DOWNTRACE_FORWARD_DIST ); + int iTraceHeight = -( pPlayer->WorldAlignMaxs().z ); + + + // search down from the forward trace + // use the farthest point first. If that fails, move towards the player a few times + // to see if they are trying to deploy on a thin railing + + bool bFound = false; + + int maxAttempts = 4; + float flHighestTraceEnd = vecDownTraceStart.z + iTraceHeight; + CBaseEntity *pBestDeployEnt = NULL; + + while( maxAttempts > 0 ) + { + UTIL_TraceHull( vecDownTraceStart, + vecDownTraceStart + Vector(0,0,iTraceHeight), // trace forward one box width + -vecDeployTraceBoxSize, + vecDeployTraceBoxSize, + traceMask, + filter, + &tr ); + +#ifndef CLIENT_DLL + if ( dod_debugmgdeploy.GetBool() ) + { + NDebugOverlay::Line( vecDownTraceStart, tr.endpos, 255, 0, 0, true, 0.1 ); + NDebugOverlay::Box( vecDownTraceStart, -vecDeployTraceBoxSize, vecDeployTraceBoxSize, 255, 0, 0, 128, 0.1 ); + NDebugOverlay::Box( tr.endpos, -vecDeployTraceBoxSize, vecDeployTraceBoxSize, 0, 0, 255, 128, 0.1 ); + } +#endif + + bool bSuccess = ( tr.fraction < 1.0 ) && !tr.startsolid && !tr.allsolid; + + // if this is the first one found, set found flag + if ( bSuccess && !bFound ) + { + bFound = true; + } + else if ( bFound == true && bSuccess == false ) + { + // it failed and we have some data. break here + break; + } + + // if this trace is better ( higher ) use this one + if ( tr.endpos.z > flHighestTraceEnd ) + { + flHighestTraceEnd = tr.endpos.z; + pBestDeployEnt = tr.m_pEnt; + } + + --maxAttempts; + + // move towards the player, looking for a better height to deploy on + vecDownTraceStart += forward * -4; + } + + if ( bFound == false || pBestDeployEnt == NULL ) + return false; + + *pDeployedOn = pBestDeployEnt; + *flDeployedHeight = flHighestTraceEnd - vecDeployTraceBoxSize[0] + DEPLOY_DOWNTRACE_OFFSET - pPlayer->GetAbsOrigin().z; + return true; +} + +Activity CDODBipodWeapon::GetUndeployActivity( void ) +{ + return ACT_VM_UNDEPLOY; +} + +Activity CDODBipodWeapon::GetDeployActivity( void ) +{ + return ACT_VM_DEPLOY; +} + + +Activity CDODBipodWeapon::GetPrimaryAttackActivity( void ) +{ + Activity actPrim; + + if( IsDeployed() ) + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED; + else + actPrim = ACT_VM_PRIMARYATTACK; + + return actPrim; +} + +Activity CDODBipodWeapon::GetReloadActivity( void ) +{ + Activity actReload; + + if( IsDeployed() ) + actReload = ACT_VM_RELOAD_DEPLOYED; + else + actReload = ACT_VM_RELOAD; + + return actReload; +} + +Activity CDODBipodWeapon::GetIdleActivity( void ) +{ + Activity actIdle; + + if( IsDeployed() ) + actIdle = ACT_VM_IDLE_DEPLOYED; + else + actIdle = ACT_VM_IDLE; + + return actIdle; +} + + +float CDODBipodWeapon::GetWeaponAccuracy( float flPlayerSpeed ) +{ + float flSpread = BaseClass::GetWeaponAccuracy( flPlayerSpeed ); + + if( IsDeployed() ) + { + flSpread = m_pWeaponInfo->m_flSecondaryAccuracy; + } + + return flSpread; +} + +#ifdef CLIENT_DLL + + int CDODBipodWeapon::GetWorldModelIndex( void ) + { + if( GetOwner() == NULL ) + return m_iWorldModelIndex; + else if( m_bUseAltWeaponModel ) + return m_iWorldModelIndex; //override for hand signals etc + else + return m_iCurrentWorldModel; + } + + void CDODBipodWeapon::CheckForAltWeapon( int iCurrentState ) + { + int iCriteria = GetDODWpnData().m_iAltWpnCriteria; + + bool bReloading = ( iCurrentState & ALTWPN_CRITERIA_RELOADING ); + + if( bReloading ) + { + if( IsDeployed() && iCurrentState & ALTWPN_CRITERIA_PRONE && + iCriteria & ALTWPN_CRITERIA_PRONE_DEPLOYED_RELOAD ) + { + m_iCurrentWorldModel = m_iProneDeployedReloadModelIndex; // prone deployed reload + } + else if( IsDeployed() && iCriteria & ALTWPN_CRITERIA_DEPLOYED_RELOAD ) + { + m_iCurrentWorldModel = m_iDeployedReloadModelIndex; // deployed reload + } + else if( iCriteria & ALTWPN_CRITERIA_RELOADING ) + { + m_iCurrentWorldModel = m_iReloadModelIndex; // left handed reload + } + else + { + m_iCurrentWorldModel = m_iWorldModelIndex; // normal weapon reload + } + } + else if( IsDeployed() && iCriteria & ALTWPN_CRITERIA_DEPLOYED ) + { + m_iCurrentWorldModel = m_iDeployedModelIndex; // bipod down + } + else if( (iCurrentState & iCriteria) & ALTWPN_CRITERIA_FIRING ) + { + // don't think we have any weapons that do this + m_iCurrentWorldModel = m_iReloadModelIndex; // left handed shooting? + } + else + { + m_iCurrentWorldModel = m_iWorldModelIndex; // normal weapon + } + } + + ConVar deployed_mg_sensitivity( "deployed_mg_sensitivity", "0.9", FCVAR_CHEAT, "Mouse sensitivity while deploying a machine gun" ); + + void CDODBipodWeapon::OverrideMouseInput( float *x, float *y ) + { + if( IsDeployed() ) + { + float flSensitivity = deployed_mg_sensitivity.GetFloat(); + + *x *= flSensitivity; + *y *= flSensitivity; + } + } + +#endif diff --git a/game/shared/dod/weapon_dodbipodgun.h b/game/shared/dod/weapon_dodbipodgun.h new file mode 100644 index 0000000..7c0bb56 --- /dev/null +++ b/game/shared/dod/weapon_dodbipodgun.h @@ -0,0 +1,92 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "shake.h" +#include "weapon_dodfullauto.h" + +#if defined( CLIENT_DLL ) + #define CDODBipodWeapon C_DODBipodWeapon +#endif + +class CDODBipodWeapon : public CDODFullAutoWeapon +{ +public: + DECLARE_CLASS( CDODBipodWeapon, CDODFullAutoWeapon ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + + CDODBipodWeapon(); + + virtual void Spawn(); + virtual void Precache(); + virtual void Drop( const Vector &vecVelocity ); + virtual void SecondaryAttack( void ); + virtual bool Reload( void ); + + virtual float GetWeaponAccuracy( float flPlayerSpeed ); + + virtual Activity GetUndeployActivity( void ); + virtual Activity GetDeployActivity( void ); + virtual Activity GetPrimaryAttackActivity( void ); + virtual Activity GetReloadActivity( void ); + virtual Activity GetIdleActivity( void ); + + virtual bool CanDrop( void ); + virtual bool CanHolster( void ); + + inline void SetDeployed( bool bDeployed ); + inline bool IsDeployed( void ) { return m_bDeployed; } + + virtual void ItemBusyFrame( void ); + virtual void ItemPostFrame( void ); + void BipodThink( void ); + + bool AttemptToDeploy( void ); + bool CheckDeployEnt( void ); + + bool TestDeploy( float *flDeployedHeight, CBaseEntity **pDeployedOn, float *flYawLimitLeft = NULL, float *flYawLimitRight = NULL ); + bool TestDeployAngle( CDODPlayer *pPlayer, float *flDeployedHeight, CBaseEntity **pDeployedOn, QAngle angles ); + + bool FindYawLimits( float *flLeftLimit, float *flRightLimit ); + + virtual void DoFireEffects(); + + void DeployBipod( float flHeight, CBaseEntity *pDeployedOn, float flYawLimitLeft, float flYawLimitRight ); + void UndeployBipod( void ); + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_NONE; } + +#ifdef CLIENT_DLL + virtual int GetWorldModelIndex( void ); + virtual void CheckForAltWeapon( int iCurrentState ); + + virtual void OverrideMouseInput( float *x, float *y ); +#endif + +private: + CDODBipodWeapon( const CDODBipodWeapon & ); + + CNetworkVar( bool, m_bDeployed ); + + CNetworkVar( int, m_iDeployedModelIndex ); + CNetworkVar( int, m_iDeployedReloadModelIndex ); + CNetworkVar( int, m_iProneDeployedReloadModelIndex ); + + int m_iCurrentWorldModel; + + EHANDLE m_hDeployedOnEnt; + float m_flDeployedHeight; + float m_flNextDeployCheckTime; + + Vector m_DeployedEntOrigin; + + bool m_bDuckedWhenDeployed; + +#ifdef CLIENT_DLL + bool m_bUseDeployedReload; +#endif +}; diff --git a/game/shared/dod/weapon_dodfireselect.cpp b/game/shared/dod/weapon_dodfireselect.cpp new file mode 100644 index 0000000..9b1038a --- /dev/null +++ b/game/shared/dod/weapon_dodfireselect.cpp @@ -0,0 +1,211 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "fx_dod_shared.h" +#include "weapon_dodfireselect.h" + +#ifdef CLIENT_DLL + #include "prediction.h" +#endif + +IMPLEMENT_NETWORKCLASS_ALIASED( DODFireSelectWeapon, DT_FireSelectWeapon ) + + +BEGIN_NETWORK_TABLE( CDODFireSelectWeapon, DT_FireSelectWeapon ) +#ifdef CLIENT_DLL + RecvPropBool( RECVINFO( m_bSemiAuto ) ) +#else + SendPropBool( SENDINFO( m_bSemiAuto ) ) +#endif +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CDODFireSelectWeapon ) +END_PREDICTION_DATA() + + +CDODFireSelectWeapon::CDODFireSelectWeapon() +{ +} + +void CDODFireSelectWeapon::Spawn( void ) +{ + m_bSemiAuto = false; + +#ifdef CLIENT_DLL + ResetViewModelAnimDir(); +#endif + + m_iAltFireHint = HINT_USE_SEMI_AUTO; + + BaseClass::Spawn(); +} + +void CDODFireSelectWeapon::PrimaryAttack( void ) +{ + if ( IsSemiAuto() ) + { + // If semi auto, set this flag which will prevent us from + // attacking again until the button is released. + m_bInAttack = true; + } + + BaseClass::PrimaryAttack(); +} + +float CDODFireSelectWeapon::GetFireDelay( void ) +{ + if ( IsSemiAuto() ) + { + return m_pWeaponInfo->m_flSecondaryFireDelay; + } + else + { + return m_pWeaponInfo->m_flFireDelay; + } +} + +void CDODFireSelectWeapon::SecondaryAttack( void ) +{ + // toggle fire mode ( full auto, semi auto ) + m_bSemiAuto = !m_bSemiAuto; + +#ifndef CLIENT_DLL + CDODPlayer *pPlayer = GetDODPlayerOwner(); + if ( pPlayer ) + { + pPlayer->RemoveHintTimer( m_iAltFireHint ); + } +#endif + + if ( m_bSemiAuto ) + { +#ifdef CLIENT_DLL + if ( prediction->IsFirstTimePredicted() ) + { + m_flPosChangeTimer = gpGlobals->curtime; + m_bAnimToSemiAuto = false; + } +#endif + + SendWeaponAnim( ACT_VM_UNDEPLOY ); + } + else + { +#ifdef CLIENT_DLL + if ( prediction->IsFirstTimePredicted() ) + { + m_flPosChangeTimer = gpGlobals->curtime; + m_bAnimToSemiAuto = true; + } +#endif + + SendWeaponAnim( ACT_VM_DEPLOY ); + } + +#ifdef CLIENT_DLL + if ( prediction->IsFirstTimePredicted() ) + { + m_flPosChangeTimer = gpGlobals->curtime; + } +#endif + + m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration(); + m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); +} + +bool CDODFireSelectWeapon::IsSemiAuto( void ) +{ + return m_bSemiAuto; +} + +float CDODFireSelectWeapon::GetWeaponAccuracy( float flPlayerSpeed ) +{ + float flSpread; + + if ( IsSemiAuto() ) + flSpread = m_pWeaponInfo->m_flSecondaryAccuracy; + else + flSpread = m_pWeaponInfo->m_flAccuracy; + + if( flPlayerSpeed > 45 ) + flSpread += m_pWeaponInfo->m_flAccuracyMovePenalty; + + return flSpread; +} + +Activity CDODFireSelectWeapon::GetIdleActivity( void ) +{ + if ( !IsSemiAuto() ) + return ACT_VM_IDLE_DEPLOYED; + + return BaseClass::GetIdleActivity(); +} + +Activity CDODFireSelectWeapon::GetPrimaryAttackActivity( void ) +{ + if ( !IsSemiAuto() ) + return ACT_VM_PRIMARYATTACK_DEPLOYED; + + return BaseClass::GetPrimaryAttackActivity(); +} + +Activity CDODFireSelectWeapon::GetReloadActivity( void ) +{ + if ( !IsSemiAuto() ) + return ACT_VM_RELOAD_DEPLOYED; + + return BaseClass::GetReloadActivity(); +} + +Activity CDODFireSelectWeapon::GetDrawActivity( void ) +{ + if ( !IsSemiAuto() ) + return ACT_VM_DRAW_DEPLOYED; + + return BaseClass::GetDrawActivity(); +} + +void CDODFireSelectWeapon::Drop( const Vector &vecVelocity ) +{ + // always full auto when you pick up a weapon + m_bSemiAuto = false; + + return BaseClass::Drop( vecVelocity ); +} + +#ifdef CLIENT_DLL + Vector CDODFireSelectWeapon::GetDesiredViewModelOffset( C_DODPlayer *pOwner ) + { + Vector viewOffset = pOwner->GetViewOffset(); + + float flPercent = ( viewOffset.z - VEC_PRONE_VIEW_SCALED( pOwner ).z ) / ( VEC_VIEW_SCALED( pOwner ).z - VEC_PRONE_VIEW_SCALED( pOwner ).z ); + + Vector offset = ( flPercent * GetDODWpnData().m_vecViewNormalOffset + + ( 1.0 - flPercent ) * GetDODWpnData().m_vecViewProneOffset ); + + static float flLastPercent = 0; + + if ( prediction->InPrediction() ) + { + return ( flLastPercent * offset + + ( 1.0 - flLastPercent ) * GetDODWpnData().m_vecViewProneOffset ); + } + + float timer = gpGlobals->curtime - m_flPosChangeTimer; + + // how long since we changed iron sight mode + float flPosChangePercent = clamp( ( timer / ( 0.3 ) ), 0.0, 1.0 ); + + float flZoomPercent = ( m_bAnimToSemiAuto ? ( 1.0 - flPosChangePercent ) : flPosChangePercent ); + + // store this value to use when called in prediction + flLastPercent = flZoomPercent; + + return ( flZoomPercent * GetDODWpnData().m_vecIronSightOffset + + ( 1.0 - flZoomPercent ) * offset ); + } +#endif diff --git a/game/shared/dod/weapon_dodfireselect.h b/game/shared/dod/weapon_dodfireselect.h new file mode 100644 index 0000000..2e00edb --- /dev/null +++ b/game/shared/dod/weapon_dodfireselect.h @@ -0,0 +1,73 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef WEAPON_DODFIRESELECT_H +#define WEAPON_DODFIRESELECT_H + +#include "cbase.h" +#include "weapon_dodbasegun.h" + +#if defined( CLIENT_DLL ) +#define CDODFireSelectWeapon C_DODFireSelectWeapon + +#include "c_dod_player.h" +#endif + +class CDODFireSelectWeapon : public CWeaponDODBaseGun +{ +public: + DECLARE_CLASS( CDODFireSelectWeapon, CWeaponDODBaseGun ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + + CDODFireSelectWeapon(); + + virtual void Spawn(); + virtual void PrimaryAttack( void ); + virtual void SecondaryAttack( void ); + virtual float GetWeaponAccuracy( float flPlayerSpeed ); + virtual float GetFireDelay( void ); + virtual void Drop( const Vector &vecVelocity ); + + bool IsSemiAuto( void ); + + virtual Activity GetIdleActivity( void ); + virtual Activity GetPrimaryAttackActivity( void ); + virtual Activity GetReloadActivity( void ); + virtual Activity GetDrawActivity( void ); + +#ifdef CLIENT_DLL + virtual Vector GetDesiredViewModelOffset( C_DODPlayer *pOwner ); + + void ResetViewModelAnimDir( void ) + { + m_bAnimToSemiAuto = true; + m_flPosChangeTimer = 0; + } + + virtual void OnWeaponDropped( void ) + { + ResetViewModelAnimDir(); + + m_bSemiAuto = false; + + BaseClass::OnWeaponDropped(); + } +#endif + +private: + CNetworkVar( bool, m_bSemiAuto ); + +#ifdef CLIENT_DLL + bool m_bAnimToSemiAuto; + float m_flPosChangeTimer; +#endif + +private: + CDODFireSelectWeapon( const CDODFireSelectWeapon & ); +}; + +#endif //WEAPON_DODFIRESELECT_H
\ No newline at end of file diff --git a/game/shared/dod/weapon_dodfullauto.cpp b/game/shared/dod/weapon_dodfullauto.cpp new file mode 100644 index 0000000..08e2b4b --- /dev/null +++ b/game/shared/dod/weapon_dodfullauto.cpp @@ -0,0 +1,32 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "fx_dod_shared.h" +#include "weapon_dodfullauto.h" + +IMPLEMENT_NETWORKCLASS_ALIASED( DODFullAutoWeapon, DT_FullAutoWeapon ) + +BEGIN_NETWORK_TABLE( CDODFullAutoWeapon, DT_FullAutoWeapon ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CDODFullAutoWeapon ) +END_PREDICTION_DATA() + + +CDODFullAutoWeapon::CDODFullAutoWeapon() +{ +} + +void CDODFullAutoWeapon::Spawn() +{ + BaseClass::Spawn(); +} + +void CDODFullAutoWeapon::PrimaryAttack( void ) +{ + BaseClass::PrimaryAttack(); +}
\ No newline at end of file diff --git a/game/shared/dod/weapon_dodfullauto.h b/game/shared/dod/weapon_dodfullauto.h new file mode 100644 index 0000000..6cf36a8 --- /dev/null +++ b/game/shared/dod/weapon_dodfullauto.h @@ -0,0 +1,36 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef WEAPON_DODFULLAUTO_H +#define WEAPON_DODFULLAUTO_H + +#include "cbase.h" +#include "shake.h" +#include "weapon_dodbasegun.h" + +#if defined( CLIENT_DLL ) + #define CDODFullAutoWeapon C_DODFullAutoWeapon +#endif + +class CDODFullAutoWeapon : public CWeaponDODBaseGun +{ +public: + DECLARE_CLASS( CDODFullAutoWeapon, CWeaponDODBaseGun ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + + CDODFullAutoWeapon(); + + virtual void Spawn(); + virtual void PrimaryAttack( void ); + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_NONE; } + +private: + CDODFullAutoWeapon( const CDODFullAutoWeapon & ); +}; + +#endif //WEAPON_DODFULLAUTO_H
\ No newline at end of file diff --git a/game/shared/dod/weapon_dodfullauto_punch.cpp b/game/shared/dod/weapon_dodfullauto_punch.cpp new file mode 100644 index 0000000..9f67a3a --- /dev/null +++ b/game/shared/dod/weapon_dodfullauto_punch.cpp @@ -0,0 +1,84 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodfullauto_punch.h" +#include "in_buttons.h" +#include "dod_shareddefs.h" + +#ifndef CLIENT_DLL +#include "dod_player.h" +#endif + +IMPLEMENT_NETWORKCLASS_ALIASED( DODFullAutoPunchWeapon, DT_FullAutoPunchWeapon ) + +BEGIN_NETWORK_TABLE( CDODFullAutoPunchWeapon, DT_FullAutoPunchWeapon ) +END_NETWORK_TABLE() + +#ifdef CLIENT_DLL +BEGIN_PREDICTION_DATA( CDODFullAutoPunchWeapon ) +END_PREDICTION_DATA() +#endif + +void CDODFullAutoPunchWeapon::Spawn( void ) +{ + m_iAltFireHint = HINT_USE_MELEE; + + BaseClass::Spawn(); +} + +void CDODFullAutoPunchWeapon::SecondaryAttack( void ) +{ + if ( m_bInReload ) + { + m_bInReload = false; + GetPlayerOwner()->m_flNextAttack = gpGlobals->curtime; + } + else if ( GetPlayerOwner()->m_flNextAttack > gpGlobals->curtime ) + { + return; + } + + Punch(); + + // start calling ItemPostFrame + GetPlayerOwner()->m_flNextAttack = gpGlobals->curtime; + + m_flNextPrimaryAttack = m_flNextSecondaryAttack; + +#ifndef CLIENT_DLL + CDODPlayer *pPlayer = GetDODPlayerOwner(); + if ( pPlayer ) + { + pPlayer->RemoveHintTimer( m_iAltFireHint ); + } +#endif +} + +bool CDODFullAutoPunchWeapon::Reload( void ) +{ + bool bSuccess = BaseClass::Reload(); + + if ( bSuccess ) + { + m_flNextSecondaryAttack = gpGlobals->curtime; + } + + return bSuccess; +} + +void CDODFullAutoPunchWeapon::ItemBusyFrame( void ) +{ + BaseClass::ItemBusyFrame(); + + CBasePlayer *pPlayer = GetPlayerOwner(); + + if ( pPlayer && (pPlayer->m_nButtons & IN_ATTACK2) && (m_flNextSecondaryAttack <= gpGlobals->curtime)) + { + SecondaryAttack(); + pPlayer->m_nButtons &= ~IN_ATTACK2; + } +} diff --git a/game/shared/dod/weapon_dodfullauto_punch.h b/game/shared/dod/weapon_dodfullauto_punch.h new file mode 100644 index 0000000..5dcee58 --- /dev/null +++ b/game/shared/dod/weapon_dodfullauto_punch.h @@ -0,0 +1,34 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodfullauto.h" + +#if defined( CLIENT_DLL ) + #define CDODFullAutoPunchWeapon C_DODFullAutoPunchWeapon +#endif + +class CDODFullAutoPunchWeapon : public CDODFullAutoWeapon +{ +public: + DECLARE_CLASS( CDODFullAutoPunchWeapon, CDODFullAutoWeapon ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + + CDODFullAutoPunchWeapon() {} + + virtual void Spawn( void ); + virtual void SecondaryAttack( void ); + virtual bool Reload( void ); + virtual void ItemBusyFrame( void ); + + virtual const char *GetSecondaryDeathNoticeName( void ) { return "punch"; } + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_NONE; } + +private: + CDODFullAutoPunchWeapon( const CDODFullAutoPunchWeapon & ); +};
\ No newline at end of file diff --git a/game/shared/dod/weapon_dodsemiauto.cpp b/game/shared/dod/weapon_dodsemiauto.cpp new file mode 100644 index 0000000..e0aaeaf --- /dev/null +++ b/game/shared/dod/weapon_dodsemiauto.cpp @@ -0,0 +1,37 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodsemiauto.h" + +IMPLEMENT_NETWORKCLASS_ALIASED( DODSemiAutoWeapon, DT_SemiAutoWeapon ) + +BEGIN_NETWORK_TABLE( CDODSemiAutoWeapon, DT_SemiAutoWeapon ) +END_NETWORK_TABLE() + +#ifdef CLIENT_DLL +BEGIN_PREDICTION_DATA( CDODSemiAutoWeapon ) +END_PREDICTION_DATA() +#endif + +CDODSemiAutoWeapon::CDODSemiAutoWeapon() +{ +} + +void CDODSemiAutoWeapon::Spawn() +{ + BaseClass::Spawn(); +} + +void CDODSemiAutoWeapon::PrimaryAttack( void ) +{ + //Don't attack more than once on the same button press. + //m_bInAttack is set to false when the attack button is released + + m_bInAttack = true; + + BaseClass::PrimaryAttack(); +}
\ No newline at end of file diff --git a/game/shared/dod/weapon_dodsemiauto.h b/game/shared/dod/weapon_dodsemiauto.h new file mode 100644 index 0000000..bda1437 --- /dev/null +++ b/game/shared/dod/weapon_dodsemiauto.h @@ -0,0 +1,40 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef WEAPON_DODSEMIAUTO_H +#define WEAPON_DODSEMIAUTO_H +#ifdef _WIN32 +#pragma once +#endif + +#include "cbase.h" +#include "shake.h" +#include "weapon_dodbasegun.h" + + +#if defined( CLIENT_DLL ) + #define CDODSemiAutoWeapon C_DODSemiAutoWeapon +#endif + +class CDODSemiAutoWeapon : public CWeaponDODBaseGun +{ +public: + DECLARE_CLASS( CDODSemiAutoWeapon, CWeaponDODBaseGun ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + + CDODSemiAutoWeapon(); + + virtual void Spawn(); + virtual void PrimaryAttack( void ); + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_NONE; } + +private: + CDODSemiAutoWeapon( const CDODSemiAutoWeapon & ); +}; + +#endif //WEAPON_DODSEMIAUTO_H
\ No newline at end of file diff --git a/game/shared/dod/weapon_dodsniper.cpp b/game/shared/dod/weapon_dodsniper.cpp new file mode 100644 index 0000000..70d3568 --- /dev/null +++ b/game/shared/dod/weapon_dodsniper.cpp @@ -0,0 +1,414 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "fx_dod_shared.h" +#include "weapon_dodsniper.h" + +#ifdef CLIENT_DLL + #include "prediction.h" +#endif + +IMPLEMENT_NETWORKCLASS_ALIASED( DODSniperWeapon, DT_SniperWeapon ) + +#ifdef CLIENT_DLL + + void RecvProxy_AnimStart( const CRecvProxyData *pData, void *pStruct, void *pOut ) + { + CDODSniperWeapon *pWpn = (CDODSniperWeapon *) pStruct; + pWpn->m_bDoViewAnim = ( pData->m_Value.m_Int > 0 ); + } + +#endif + +BEGIN_NETWORK_TABLE( CDODSniperWeapon, DT_SniperWeapon ) + #ifdef CLIENT_DLL + RecvPropBool( RECVINFO( m_bZoomed ) ), + RecvPropInt( RECVINFO( m_bDoViewAnim ), 0, RecvProxy_AnimStart ) + #else + SendPropBool( SENDINFO( m_bZoomed ) ), + SendPropBool( SENDINFO( m_bDoViewAnim ) ) + #endif +END_NETWORK_TABLE() + + +#ifdef CLIENT_DLL + + BEGIN_PREDICTION_DATA( CDODSniperWeapon ) + DEFINE_PRED_FIELD( m_bZoomed, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ) + END_PREDICTION_DATA() + +#else + + BEGIN_DATADESC( CDODSniperWeapon ) + END_DATADESC() + +#endif + + +CDODSniperWeapon::CDODSniperWeapon() +{ +} + +void CDODSniperWeapon::Spawn( void ) +{ + BaseClass::Spawn(); + + ResetTimers(); + +#ifdef CLIENT_DLL + m_bDoViewAnimCache = false; + m_flZoomPercent = 0.0f; +#endif + + m_bShouldRezoomAfterShot = true; +} + +void CDODSniperWeapon::ResetTimers( void ) +{ + m_flUnzoomTime = -1; + m_flRezoomTime = -1; + m_flZoomOutTime = -1; + m_flZoomInTime = -1; + m_bRezoomAfterShot = false; + m_bRezoomAfterReload = false; +} + +bool CDODSniperWeapon::Holster( CBaseCombatWeapon *pSwitchingTo ) +{ +#ifndef CLIENT_DLL + ZoomOut(); + + ResetTimers(); +#endif + + return BaseClass::Holster( pSwitchingTo ); +} + +void CDODSniperWeapon::PrimaryAttack( void ) +{ + BaseClass::PrimaryAttack(); + + CDODPlayer *pPlayer = ToDODPlayer( GetOwner() ); + + if ( IsZoomed() && ShouldZoomOutBetweenShots() ) + { + //If we have more bullets, zoom out, play the bolt animation and zoom back in + if( m_iClip1 > 0 && m_bShouldRezoomAfterShot && ( pPlayer && pPlayer->ShouldAutoRezoom() ) ) + { + SetRezoom( true, 0.5f ); // zoom out in 0.5 seconds, then rezoom + } + else //just zoom out + { + SetRezoom( false, 0.5f ); // just zoom out in 0.5 seconds + } + } + + // overwrite the next secondary attack, so we can zoom sooned after we've fired + if ( m_bRezoomAfterShot && m_iClip1 > 0 ) + m_flNextSecondaryAttack = gpGlobals->curtime + 2.0; + else + m_flNextSecondaryAttack = SequenceDuration(); +} + +void CDODSniperWeapon::SecondaryAttack( void ) +{ + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); + + Assert( pPlayer ); + + if ( !pPlayer ) + return; + + ToggleZoom(); +} + +bool CDODSniperWeapon::Reload( void ) +{ + bool bSuccess = BaseClass::Reload(); + + if ( bSuccess && IsZoomed() ) + { + //if ( ShouldRezoomAfterReload() ) + // m_bRezoomAfterReload = true; + + ZoomOut(); + } + + if ( !bSuccess ) + m_bRezoomAfterReload = false; + + return bSuccess; +} + +void CDODSniperWeapon::FinishReload( void ) +{ + BaseClass::FinishReload(); + + if ( m_bRezoomAfterReload ) + { + ZoomIn(); + m_bRezoomAfterReload = false; + } +} + +float CDODSniperWeapon::GetWeaponAccuracy( float flPlayerSpeed ) +{ + //snipers and deployable weapons inherit this and override when we need a different accuracy + + float flSpread = m_pWeaponInfo->m_flAccuracy; + + if ( IsZoomed() && ( gpGlobals->curtime - m_flZoomChangeTime ) > DOD_SNIPER_ZOOM_CHANGE_TIME ) + { + flSpread = m_pWeaponInfo->m_flSecondaryAccuracy; + } + + if( flPlayerSpeed > 45 ) + flSpread += m_pWeaponInfo->m_flAccuracyMovePenalty; + + return flSpread; +} + +bool CDODSniperWeapon::IsZoomed( void ) +{ + // check the player? + return IsSniperZoomed(); +} + +bool CDODSniperWeapon::ShouldDrawCrosshair( void ) +{ + //if ( IsFullyZoomed() ) + // return false; + + // don't draw if we are zoomed at all + if ( m_bZoomed ) + return false; + + return BaseClass::ShouldDrawCrosshair(); +} + +#include "in_buttons.h" + +void CDODSniperWeapon::ItemPostFrame( void ) +{ + if ( m_flUnzoomTime > 0 && gpGlobals->curtime > m_flUnzoomTime ) + { + if ( m_bRezoomAfterShot ) + { + ZoomOutIn(); + m_bRezoomAfterShot = false; + } + else + { + ZoomOut(); + } + + m_flUnzoomTime = -1; + } + + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + Assert( pPlayer ); + + if ( m_flRezoomTime > 0 ) + { + // if the player is sprinting and moving at all, cancel the rezoom + if ( ( pPlayer->m_nButtons & IN_SPEED ) > 0 && + pPlayer->GetAbsVelocity().Length2D() > 20 ) + { + m_flRezoomTime = -1; + } + else if ( gpGlobals->curtime > m_flRezoomTime ) + { + ZoomIn(); + m_flRezoomTime = -1; + } + } + + if ( m_flZoomInTime > 0 && gpGlobals->curtime > m_flZoomInTime ) + { +#ifndef CLIENT_DLL + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + Assert( pPlayer ); + pPlayer->SetFOV( pPlayer, GetZoomedFOV(), 0.1f ); +#endif + m_flZoomInTime = -1; + m_flZoomOutTime = -1; + } + + if ( m_flZoomInTime > 0 && (pPlayer->m_nButtons & IN_ATTACK) ) + { + m_bInAttack = true; + } + + BaseClass::ItemPostFrame(); +} + +void CDODSniperWeapon::Drop( const Vector &vecVelocity ) +{ + // If a player is killed while deployed, this resets the weapon state + if ( IsZoomed() ) + ZoomOut(); + + ResetTimers(); + + BaseClass::Drop( vecVelocity ); +} + +void CDODSniperWeapon::ToggleZoom( void ) +{ + CDODPlayer *pPlayer = GetDODPlayerOwner(); + + if ( !pPlayer->m_Shared.IsJumping() ) + { + if( !IsZoomed() ) + { + if ( CanAttack() ) + { +#ifndef CLIENT_DLL + pPlayer->RemoveHintTimer( m_iAltFireHint ); +#endif + + ZoomIn(); + } + } + else + { + ZoomOut(); + } + } +} + +void CDODSniperWeapon::ZoomIn( void ) +{ + m_flZoomInTime = gpGlobals->curtime + DOD_SNIPER_ZOOM_CHANGE_TIME; + +#ifndef CLIENT_DLL + SetZoomed( true ); + + m_bDoViewAnim = !m_bDoViewAnim; +#endif + + m_flNextPrimaryAttack = MAX( gpGlobals->curtime + 0.5, m_flNextPrimaryAttack ); + m_flNextSecondaryAttack = MAX( gpGlobals->curtime + 0.5, m_flNextSecondaryAttack ); + m_flTimeWeaponIdle = gpGlobals->curtime + m_pWeaponInfo->m_flTimeToIdleAfterFire; + + m_flZoomChangeTime = gpGlobals->curtime; +} + +void CDODSniperWeapon::ZoomOut( void ) +{ + bool bWasZoomed = IsZoomed(); + +#ifndef CLIENT_DLL + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + Assert( pPlayer ); + + pPlayer->SetFOV( pPlayer, 90, 0.1f ); + SetZoomed( false ); + + m_bDoViewAnim = !m_bDoViewAnim; +#endif + + if ( bWasZoomed ) + { + m_flNextPrimaryAttack = MAX( gpGlobals->curtime + 0.5, m_flNextPrimaryAttack ); + m_flNextSecondaryAttack = MAX( gpGlobals->curtime + 0.5, m_flNextSecondaryAttack ); + m_flTimeWeaponIdle = gpGlobals->curtime + m_pWeaponInfo->m_flTimeToIdleAfterFire; + } + + m_flZoomChangeTime = gpGlobals->curtime; + + // if we are thinking about zooming, cancel it + m_flZoomInTime = -1; + m_flUnzoomTime = -1; + m_flRezoomTime = -1; +} + +// reduce rezoome time, to account for fade in we're replacing with anim +#define REZOOM_TIME 1.2f + +void CDODSniperWeapon::ZoomOutIn( void ) +{ + ZoomOut(); + + m_flRezoomTime = gpGlobals->curtime + 1.0; +} + +void CDODSniperWeapon::SetRezoom( bool bRezoom, float flDelay ) +{ + m_flUnzoomTime = gpGlobals->curtime + flDelay; + + m_bRezoomAfterShot = bRezoom; +} + +bool CDODSniperWeapon::IsFullyZoomed( void ) +{ + return ( IsZoomed() == true && + (( gpGlobals->curtime - m_flZoomChangeTime ) > DOD_SNIPER_ZOOM_CHANGE_TIME) ); +} + +bool CDODSniperWeapon::IsZoomingIn( void ) +{ + return ( m_flZoomInTime > gpGlobals->curtime ); +} + +#ifdef CLIENT_DLL + + float CDODSniperWeapon::GetZoomedPercentage( void ) + { + return m_flZoomPercent; + } + + Vector CDODSniperWeapon::GetDesiredViewModelOffset( C_DODPlayer *pOwner ) + { + static Vector vecLastResult = vec3_origin; + + if ( prediction->InPrediction() && !prediction->IsFirstTimePredicted() ) + { + return vecLastResult; + } + + if ( m_bDoViewAnim != m_bDoViewAnimCache ) + { + // start the anim timer + m_flViewAnimTimer = gpGlobals->curtime + DOD_SNIPER_ZOOM_CHANGE_TIME; + m_bDoViewAnimCache = m_bDoViewAnim.m_Value; + } + + Vector viewOffset = pOwner->GetViewOffset(); + + float flPercent = clamp( ( viewOffset.z - VEC_PRONE_VIEW_SCALED( pOwner ).z ) / ( VEC_VIEW_SCALED( pOwner ).z - VEC_PRONE_VIEW_SCALED( pOwner ).z ), 0.0, 1.0 ); + + Vector offset = ( flPercent * GetDODWpnData().m_vecViewNormalOffset + + ( 1.0 - flPercent ) * GetDODWpnData().m_vecViewProneOffset ); + + + // how long since we changed iron sight mode + float flZoomAnimPercent = clamp( ( (m_flViewAnimTimer - gpGlobals->curtime) / ( DOD_SNIPER_ZOOM_CHANGE_TIME ) ), 0.0, 1.0 ); + + m_flZoomPercent = ( IsZoomed() ? ( 1.0 - flZoomAnimPercent ) : flZoomAnimPercent ); + + //Msg( "(%.1f) zoom %.2f %.2f\n", gpGlobals->curtime, m_flViewAnimTimer - gpGlobals->curtime, m_flZoomPercent ); + + // use that percent to interp to iron sight position + Vector vecResult = ( m_flZoomPercent * GetDODWpnData().m_vecIronSightOffset + + ( 1.0 - m_flZoomPercent ) * offset ); + + // store this value to use when called in prediction + vecLastResult = vecResult; + + return vecResult; + } + + float CDODSniperWeapon::GetViewModelSwayScale( void ) + { + if ( IsFullyZoomed() ) + return 0; + + return BaseClass::GetViewModelSwayScale(); + } + +#endif //CLIENT_DLL diff --git a/game/shared/dod/weapon_dodsniper.h b/game/shared/dod/weapon_dodsniper.h new file mode 100644 index 0000000..da6fefe --- /dev/null +++ b/game/shared/dod/weapon_dodsniper.h @@ -0,0 +1,111 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef WEAPON_DODSNIPER_H +#define WEAPON_DODSNIPER_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "cbase.h" +#include "shake.h" +#include "weapon_dodsemiauto.h" +#include "dod_shareddefs.h" + +#if defined( CLIENT_DLL ) + #define CDODSniperWeapon C_DODSniperWeapon +#endif + +#define DOD_SNIPER_ZOOM_CHANGE_TIME 0.3 + +class CDODSniperWeapon : public CDODSemiAutoWeapon +{ +public: + DECLARE_CLASS( CDODSniperWeapon, CDODSemiAutoWeapon ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + +#ifndef CLIENT_DLL + DECLARE_DATADESC(); +#endif + + CDODSniperWeapon(); + + virtual void Spawn( void ); + virtual bool Holster( CBaseCombatWeapon *pSwitchingTo ); + virtual bool Reload( void ); + virtual void FinishReload( void ); + virtual void Drop( const Vector &vecVelocity ); + virtual void ItemPostFrame( void ); + + virtual void PrimaryAttack( void ); + virtual void SecondaryAttack( void ); + + void ResetTimers( void ); // reset all the flags, timers that would cause us to re-zoom or unzoom + +#ifdef CLIENT_DLL + virtual Vector GetDesiredViewModelOffset( C_DODPlayer *pOwner ); + virtual float GetViewModelSwayScale( void ); + + float GetZoomedPercentage( void ); +#endif + + // Is the weapon completely zoomed, finished the raising animation + bool IsFullyZoomed( void ); + + virtual bool ShouldDrawCrosshair( void ); + + virtual bool HideViewModelWhenZoomed( void ) { return true; } + + virtual float GetWeaponAccuracy( float flPlayerSpeed ); + + virtual float GetZoomedFOV( void ) { return 20; } + + bool IsZoomed( void ); + + virtual bool ShouldZoomOutBetweenShots( void ) { return true; } + virtual bool ShouldRezoomAfterReload( void ) { return false; } + + void ToggleZoom( void ); + + void ZoomIn( void ); + void ZoomOut( void ); + + void ZoomOutIn( void ); + + void SetRezoom( bool bRezoom, float flDelay ); + + bool IsZoomingIn( void ); + + CNetworkVar( bool, m_bDoViewAnim ); + + + +#ifdef CLIENT_DLL + bool m_bDoViewAnimCache; + float m_flViewAnimTimer; + float m_flZoomPercent; +#endif + +protected: + bool m_bShouldRezoomAfterShot; // if the gun zooms out after a shot, does it zoom back in automatically? + +private: + CDODSniperWeapon( const CDODSniperWeapon & ); + + float m_flUnzoomTime; + float m_flRezoomTime; + + float m_flZoomInTime; + float m_flZoomOutTime; + + bool m_bRezoomAfterReload; + float m_flZoomChangeTime; + bool m_bRezoomAfterShot; +}; + +#endif // WEAPON_DODSNIPER_H
\ No newline at end of file diff --git a/game/shared/dod/weapon_explodinghandgrenade.cpp b/game/shared/dod/weapon_explodinghandgrenade.cpp new file mode 100644 index 0000000..379f725 --- /dev/null +++ b/game/shared/dod/weapon_explodinghandgrenade.cpp @@ -0,0 +1,52 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodbasegrenade.h" + +#ifdef CLIENT_DLL + #define CWeaponExplodingHandGrenade C_WeaponExplodingHandGrenade +#else + #include "dod_handgrenade.h" //the thing that we throw +#endif + + +class CWeaponExplodingHandGrenade : public CWeaponDODBaseGrenade +{ +public: + DECLARE_CLASS( CWeaponExplodingHandGrenade, CWeaponDODBaseGrenade ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + + CWeaponExplodingHandGrenade() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_FRAG_US_LIVE; } + +#ifndef CLIENT_DLL + + virtual void EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer, float flLifeTime = GRENADE_FUSE_LENGTH ) + { + CDODHandGrenade::Create( vecSrc, vecAngles, vecVel, angImpulse, pPlayer, flLifeTime, GetWeaponID() ); + } + +#endif + + virtual bool IsArmed( void ) { return true; } + +private: + CWeaponExplodingHandGrenade( const CWeaponExplodingHandGrenade & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponExplodingHandGrenade, DT_WeaponExplodingHandGrenade ) + +BEGIN_NETWORK_TABLE(CWeaponExplodingHandGrenade, DT_WeaponExplodingHandGrenade) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponExplodingHandGrenade ) //MATTTODO: are these necessary? +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_frag_us_live, CWeaponExplodingHandGrenade ); +PRECACHE_WEAPON_REGISTER( weapon_frag_us_live );
\ No newline at end of file diff --git a/game/shared/dod/weapon_explodingstickgrenade.cpp b/game/shared/dod/weapon_explodingstickgrenade.cpp new file mode 100644 index 0000000..fa5e012 --- /dev/null +++ b/game/shared/dod/weapon_explodingstickgrenade.cpp @@ -0,0 +1,54 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodbasegrenade.h" + +#ifdef CLIENT_DLL + #define CWeaponExplodingStickGrenade C_WeaponExplodingStickGrenade +#else + #include "dod_stickgrenade.h" //the thing that we throw +#endif + +class CWeaponExplodingStickGrenade : public CWeaponDODBaseGrenade +{ +public: + DECLARE_CLASS( CWeaponExplodingStickGrenade, CWeaponDODBaseGrenade ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + + CWeaponExplodingStickGrenade() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_FRAG_GER_LIVE; } + +#ifndef CLIENT_DLL + + virtual void EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer, float flLifeTime = GRENADE_FUSE_LENGTH ) + { + // align the stickgrenade vertically and spin end over end + QAngle vecNewAngles = QAngle(45,pPlayer->EyeAngles().y,0); + AngularImpulse angNewImpulse = AngularImpulse( 0, 600, 0 ); + + CDODStickGrenade::Create( vecSrc, vecNewAngles, vecVel, angNewImpulse, pPlayer, flLifeTime, GetWeaponID() ); + } + +#endif + + virtual bool IsArmed( void ) { return true; } + + CWeaponExplodingStickGrenade( const CWeaponExplodingStickGrenade & ) {} +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponExplodingStickGrenade, DT_WeaponExplodingStickGrenade ) + +BEGIN_NETWORK_TABLE(CWeaponExplodingStickGrenade, DT_WeaponExplodingStickGrenade) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponExplodingStickGrenade ) //MATTTODO: are these necessary? +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_frag_ger_live, CWeaponExplodingStickGrenade ); +PRECACHE_WEAPON_REGISTER( weapon_frag_ger_live );
\ No newline at end of file diff --git a/game/shared/dod/weapon_garand.cpp b/game/shared/dod/weapon_garand.cpp new file mode 100644 index 0000000..7fb6ec0 --- /dev/null +++ b/game/shared/dod/weapon_garand.cpp @@ -0,0 +1,203 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodsniper.h" +#include "dod_shareddefs.h" + +#ifndef CLIENT_DLL + #include "te_effect_dispatch.h" + #include "effect_dispatch_data.h" +#endif + +#if defined( CLIENT_DLL ) + #define CWeaponGarand C_WeaponGarand + + #include "c_dod_player.h" +#endif + + +class CWeaponGarand : public CDODSniperWeapon +{ +public: + DECLARE_CLASS( CWeaponGarand, CDODSniperWeapon ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeaponGarand() {} + + virtual void Spawn( void ); + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_GARAND; } + + // weapon id for stats purposes + virtual DODWeaponID GetStatsWeaponID( void ) + { + if ( IsFullyZoomed() ) + return WEAPON_GARAND_ZOOMED; + else + return WEAPON_GARAND; + } + + virtual Activity GetIdleActivity( void ); + virtual Activity GetPrimaryAttackActivity( void ); + virtual Activity GetDrawActivity( void ); + + virtual void PrimaryAttack( void ); + virtual bool Reload( void ); + + virtual float GetZoomedFOV( void ) { return 55; } + + virtual bool HideViewModelWhenZoomed( void ) { return false; } + + virtual bool ShouldZoomOutBetweenShots( void ) { return false; } + virtual bool ShouldRezoomAfterReload( void ) { return true; } + + virtual float GetRecoil( void ) { return 4.0f; } + +private: + CWeaponGarand( const CWeaponGarand & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponGarand, DT_WeaponGarand ) + +BEGIN_NETWORK_TABLE( CWeaponGarand, DT_WeaponGarand ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponGarand ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_garand, CWeaponGarand ); +PRECACHE_WEAPON_REGISTER( weapon_garand ); + +void CWeaponGarand::Spawn( void ) +{ + m_iAltFireHint = HINT_USE_IRON_SIGHTS; + + BaseClass::Spawn(); +} + +acttable_t CWeaponGarand::m_acttable[] = +{ + { ACT_DOD_STAND_AIM, ACT_DOD_STAND_AIM_RIFLE, false }, + { ACT_DOD_CROUCH_AIM, ACT_DOD_CROUCH_AIM_RIFLE, false }, + { ACT_DOD_CROUCHWALK_AIM, ACT_DOD_CROUCHWALK_AIM_RIFLE, false }, + { ACT_DOD_WALK_AIM, ACT_DOD_WALK_AIM_RIFLE, false }, + { ACT_DOD_RUN_AIM, ACT_DOD_RUN_AIM_RIFLE, false }, + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_RIFLE, false }, + { ACT_PRONE_FORWARD, ACT_DOD_PRONEWALK_IDLE_RIFLE, false }, + { ACT_DOD_STAND_IDLE, ACT_DOD_STAND_IDLE_RIFLE, false }, + { ACT_DOD_CROUCH_IDLE, ACT_DOD_CROUCH_IDLE_RIFLE, false }, + { ACT_DOD_CROUCHWALK_IDLE, ACT_DOD_CROUCHWALK_IDLE_RIFLE, false }, + { ACT_DOD_WALK_IDLE, ACT_DOD_WALK_IDLE_RIFLE, false }, + { ACT_DOD_RUN_IDLE, ACT_DOD_RUN_IDLE_RIFLE, false }, + { ACT_SPRINT, ACT_DOD_SPRINT_IDLE_RIFLE, false }, + + // Zoomed Aim + { ACT_DOD_IDLE_ZOOMED, ACT_DOD_STAND_ZOOM_RIFLE, false }, + { ACT_DOD_CROUCH_ZOOMED, ACT_DOD_CROUCH_ZOOM_RIFLE, false }, + { ACT_DOD_CROUCHWALK_ZOOMED, ACT_DOD_CROUCHWALK_ZOOM_RIFLE, false }, + { ACT_DOD_WALK_ZOOMED, ACT_DOD_WALK_ZOOM_RIFLE, false }, + { ACT_DOD_PRONE_ZOOMED, ACT_DOD_PRONE_ZOOM_RIFLE, false }, + { ACT_DOD_PRONE_FORWARD_ZOOMED, ACT_DOD_PRONE_ZOOM_FORWARD_RIFLE, false }, + + // Attack ( prone? deployed? ) + { ACT_RANGE_ATTACK1, ACT_DOD_PRIMARYATTACK_RIFLE, false }, + { ACT_DOD_PRIMARYATTACK_CROUCH, ACT_DOD_PRIMARYATTACK_RIFLE, false }, + { ACT_DOD_PRIMARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_RIFLE, false }, + + { ACT_RELOAD, ACT_DOD_RELOAD_RIFLE, false }, + { ACT_DOD_RELOAD_CROUCH, ACT_DOD_RELOAD_CROUCH_RIFLE, false }, + { ACT_DOD_RELOAD_PRONE, ACT_DOD_RELOAD_PRONE_RIFLE, false }, + + // Hand Signals + { ACT_DOD_HS_IDLE, ACT_DOD_HS_IDLE_K98, false }, + { ACT_DOD_HS_CROUCH, ACT_DOD_HS_CROUCH_K98, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponGarand ); + +Activity CWeaponGarand::GetIdleActivity( void ) +{ + Activity actIdle; + + if( m_iClip1 <= 0 ) + actIdle = ACT_VM_IDLE_EMPTY; + else + actIdle = ACT_VM_IDLE; + + return actIdle; +} + +Activity CWeaponGarand::GetPrimaryAttackActivity( void ) +{ + Activity actPrim; + + if ( IsFullyZoomed() ) + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED; + else if( m_iClip1 <= 0 ) + actPrim = ACT_VM_PRIMARYATTACK_EMPTY; + else + actPrim = ACT_VM_PRIMARYATTACK; + + return actPrim; +} + +Activity CWeaponGarand::GetDrawActivity( void ) +{ + Activity actDraw; + + if( m_iClip1 <= 0 ) + actDraw = ACT_VM_DRAW_EMPTY; + else + actDraw = ACT_VM_DRAW; + + return actDraw; +} + +void CWeaponGarand::PrimaryAttack( void ) +{ + int clip = m_iClip1; + + BaseClass::PrimaryAttack(); + + // If we just fired our last bullet + if( clip != m_iClip1 && m_iClip1 == 0 ) + { + // clip "DING!" + WeaponSound( SPECIAL1 ); + +#ifndef CLIENT_DLL + CEffectData data; + data.m_nHitBox = EJECTBRASS_GARANDCLIP; + GetPlayerOwner()->GetAttachment( 2, data.m_vOrigin, data.m_vAngles ); + + // shoot it up in the air + data.m_vAngles.x = -90; + data.m_vAngles.y = 0; + data.m_vAngles.z = 0; + + DispatchEffect( "DOD_EjectBrass", data ); +#endif + } +} + +bool CWeaponGarand::Reload( void ) +{ + if( m_iClip1 > 0 ) + { +#ifdef CLIENT_DLL + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + Assert( pPlayer ); + pPlayer->HintMessage( HINT_GARAND_RELOAD, true ); +#endif + return false; + } + + return BaseClass::Reload(); +} + diff --git a/game/shared/dod/weapon_gerknife.cpp b/game/shared/dod/weapon_gerknife.cpp new file mode 100644 index 0000000..1c8f4e7 --- /dev/null +++ b/game/shared/dod/weapon_gerknife.cpp @@ -0,0 +1,56 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodbasemelee.h" + +#if defined( CLIENT_DLL ) + + #define CWeaponGerKnife C_WeaponGerKnife + +#endif + + +class CWeaponGerKnife : public CWeaponDODBaseMelee +{ +public: + DECLARE_CLASS( CWeaponGerKnife, CWeaponDODBaseMelee ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeaponGerKnife() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_GERKNIFE; } + +private: + CWeaponGerKnife( const CWeaponGerKnife & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponGerKnife, DT_WeaponGerKnife ) + +BEGIN_NETWORK_TABLE( CWeaponGerKnife, DT_WeaponGerKnife ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponGerKnife ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_gerknife, CWeaponGerKnife ); +PRECACHE_WEAPON_REGISTER( weapon_gerknife ); + +acttable_t CWeaponGerKnife::m_acttable[] = +{ + { ACT_IDLE, ACT_DOD_STAND_AIM_KNIFE, false }, + { ACT_CROUCHIDLE, ACT_DOD_CROUCH_AIM_KNIFE, false }, + { ACT_RUN_CROUCH, ACT_DOD_CROUCHWALK_AIM_KNIFE, false }, + { ACT_WALK, ACT_DOD_WALK_AIM_KNIFE, false }, + { ACT_RUN, ACT_DOD_RUN_AIM_KNIFE, false }, + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_KNIFE, false }, + { ACT_RANGE_ATTACK1, ACT_DOD_PRIMARYATTACK_KNIFE, false }, + { ACT_DOD_PRIMARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_KNIFE, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponGerKnife );
\ No newline at end of file diff --git a/game/shared/dod/weapon_greasegun.cpp b/game/shared/dod/weapon_greasegun.cpp new file mode 100644 index 0000000..db748f8 --- /dev/null +++ b/game/shared/dod/weapon_greasegun.cpp @@ -0,0 +1,64 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodfullauto.h" + + +#if defined( CLIENT_DLL ) + + #define CWeaponGreaseGun C_WeaponGreaseGun + +#endif + + +class CWeaponGreaseGun : public CDODFullAutoWeapon +{ +public: + DECLARE_CLASS( CWeaponGreaseGun, CDODFullAutoWeapon ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeaponGreaseGun() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_GREASEGUN; } + +private: + CWeaponGreaseGun( const CWeaponGreaseGun & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponGreaseGun, DT_WeaponGreaseGun ) + +BEGIN_NETWORK_TABLE( CWeaponGreaseGun, DT_WeaponGreaseGun ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponGreaseGun ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_greasegun, CWeaponGreaseGun ); +PRECACHE_WEAPON_REGISTER( weapon_greasegun ); + +acttable_t CWeaponGreaseGun::m_acttable[] = +{ + // Aim + { ACT_IDLE, ACT_DOD_STAND_AIM_GREASE, false }, + { ACT_CROUCHIDLE, ACT_DOD_CROUCH_AIM_GREASE, false }, + { ACT_RUN_CROUCH, ACT_DOD_CROUCHWALK_AIM_GREASE, false }, + { ACT_WALK, ACT_DOD_WALK_AIM_GREASE, false }, + { ACT_RUN, ACT_DOD_RUN_AIM_GREASE, false }, + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_GREASE, false }, + + // Attack ( prone? ) + { ACT_RANGE_ATTACK1, ACT_DOD_PRIMARYATTACK_GREASE, false }, + { ACT_DOD_PRIMARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_GREASE, false }, + + // Reload ( prone? ) + { ACT_RELOAD, ACT_DOD_RELOAD_GREASEGUN, false }, + { ACT_DOD_RELOAD_PRONE, ACT_DOD_RELOAD_PRONE_GREASEGUN, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponGreaseGun );
\ No newline at end of file diff --git a/game/shared/dod/weapon_handgrenade.cpp b/game/shared/dod/weapon_handgrenade.cpp new file mode 100644 index 0000000..f3e17c4 --- /dev/null +++ b/game/shared/dod/weapon_handgrenade.cpp @@ -0,0 +1,49 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodbasegrenade.h" + +#ifdef CLIENT_DLL + #define CWeaponHandGrenade C_WeaponHandGrenade +#else + #include "dod_handgrenade.h" //the thing that we throw +#endif + +class CWeaponHandGrenade : public CWeaponDODBaseGrenade +{ +public: + DECLARE_CLASS( CWeaponHandGrenade, CWeaponDODBaseGrenade ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + + CWeaponHandGrenade() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_FRAG_US; } + +#ifndef CLIENT_DLL + + virtual void EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer, float flLifeTime = GRENADE_FUSE_LENGTH ) + { + CDODHandGrenade::Create( vecSrc, vecAngles, vecVel, angImpulse, pPlayer, flLifeTime, GetWeaponID() ); + } + +#endif + +private: + CWeaponHandGrenade( const CWeaponHandGrenade & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponHandGrenade, DT_WeaponHandGrenade ) + +BEGIN_NETWORK_TABLE(CWeaponHandGrenade, DT_WeaponHandGrenade) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponHandGrenade ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_frag_us, CWeaponHandGrenade ); +PRECACHE_WEAPON_REGISTER( weapon_frag_us );
\ No newline at end of file diff --git a/game/shared/dod/weapon_k98.cpp b/game/shared/dod/weapon_k98.cpp new file mode 100644 index 0000000..a279568 --- /dev/null +++ b/game/shared/dod/weapon_k98.cpp @@ -0,0 +1,140 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodsniper.h" + +#if defined( CLIENT_DLL ) + + #define CWeaponK98 C_WeaponK98 + +#endif + + +class CWeaponK98 : public CDODSniperWeapon +{ +public: + DECLARE_CLASS( CWeaponK98, CDODSniperWeapon ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeaponK98() {} + + virtual void Spawn( void ); + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_K98; } + + // weapon id for stats purposes + virtual DODWeaponID GetStatsWeaponID( void ) + { + if ( IsFullyZoomed() ) + return WEAPON_K98_ZOOMED; + else + return WEAPON_K98; + } + + virtual bool ShouldAutoEjectBrass( void ) { return false; } + + virtual float GetZoomedFOV( void ) { return 55; } + + virtual bool HideViewModelWhenZoomed( void ) { return false; } + + virtual bool ShouldRezoomAfterReload( void ) { return true; } + + virtual Activity GetPrimaryAttackActivity( void ); + + virtual float GetFireDelay( void ); + + virtual float GetRecoil( void ) { return 8.0f; } + +private: + CWeaponK98( const CWeaponK98 & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponK98, DT_WeaponK98 ) + +BEGIN_NETWORK_TABLE( CWeaponK98, DT_WeaponK98 ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponK98 ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_k98, CWeaponK98 ); +PRECACHE_WEAPON_REGISTER( weapon_k98 ); + +void CWeaponK98::Spawn( void ) +{ + m_iAltFireHint = HINT_USE_IRON_SIGHTS; + + BaseClass::Spawn(); + + m_bShouldRezoomAfterShot = false; +} + +acttable_t CWeaponK98::m_acttable[] = +{ + { ACT_DOD_STAND_AIM, ACT_DOD_STAND_AIM_BOLT, false }, + { ACT_DOD_CROUCH_AIM, ACT_DOD_CROUCH_AIM_BOLT, false }, + { ACT_DOD_CROUCHWALK_AIM, ACT_DOD_CROUCHWALK_AIM_BOLT, false }, + { ACT_DOD_WALK_AIM, ACT_DOD_WALK_AIM_BOLT, false }, + { ACT_DOD_RUN_AIM, ACT_DOD_RUN_AIM_BOLT, false }, + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_BOLT, false }, + { ACT_PRONE_FORWARD, ACT_DOD_PRONEWALK_IDLE_BOLT, false }, + { ACT_DOD_STAND_IDLE, ACT_DOD_STAND_IDLE_BOLT, false }, + { ACT_DOD_CROUCH_IDLE, ACT_DOD_CROUCH_IDLE_BOLT, false }, + { ACT_DOD_CROUCHWALK_IDLE, ACT_DOD_CROUCHWALK_IDLE_BOLT, false }, + { ACT_DOD_WALK_IDLE, ACT_DOD_WALK_IDLE_BOLT, false }, + { ACT_DOD_RUN_IDLE, ACT_DOD_RUN_IDLE_BOLT, false }, + { ACT_SPRINT, ACT_DOD_SPRINT_IDLE_BOLT, false }, + + // Zoomed Aim + { ACT_DOD_IDLE_ZOOMED, ACT_DOD_STAND_ZOOM_BOLT, false }, + { ACT_DOD_CROUCH_ZOOMED, ACT_DOD_CROUCH_ZOOM_BOLT, false }, + { ACT_DOD_CROUCHWALK_ZOOMED, ACT_DOD_CROUCHWALK_ZOOM_BOLT, false }, + { ACT_DOD_WALK_ZOOMED, ACT_DOD_WALK_ZOOM_BOLT, false }, + { ACT_DOD_PRONE_ZOOMED, ACT_DOD_PRONE_ZOOM_BOLT, false }, + { ACT_DOD_PRONE_FORWARD_ZOOMED, ACT_DOD_PRONE_ZOOM_FORWARD_BOLT, false }, + + // Attack ( prone? ) + { ACT_RANGE_ATTACK1, ACT_DOD_PRIMARYATTACK_BOLT, false }, + { ACT_DOD_PRIMARYATTACK_CROUCH, ACT_DOD_PRIMARYATTACK_BOLT, false }, + { ACT_DOD_PRIMARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_BOLT, false }, + + // Reload ( prone ? ) + { ACT_RELOAD, ACT_DOD_RELOAD_BOLT, false }, + { ACT_DOD_RELOAD_CROUCH, ACT_DOD_RELOAD_CROUCH_BOLT, false }, + { ACT_DOD_RELOAD_PRONE, ACT_DOD_RELOAD_PRONE_BOLT, false }, + + // Hand Signals + { ACT_DOD_HS_IDLE, ACT_DOD_HS_IDLE_K98, false }, + { ACT_DOD_HS_CROUCH, ACT_DOD_HS_CROUCH_K98, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponK98 ); + +Activity CWeaponK98::GetPrimaryAttackActivity( void ) +{ + Activity actPrim; + + if( m_iClip1 <= 0 ) + actPrim = ACT_VM_PRIMARYATTACK_EMPTY; + else + actPrim = ACT_VM_PRIMARYATTACK; + + return actPrim; +} + +float CWeaponK98::GetFireDelay( void ) +{ + if ( m_iClip1 <= 0 ) + { + return SequenceDuration(); + } + + return BaseClass::GetFireDelay(); +} + diff --git a/game/shared/dod/weapon_k98_scoped.cpp b/game/shared/dod/weapon_k98_scoped.cpp new file mode 100644 index 0000000..b3246f1 --- /dev/null +++ b/game/shared/dod/weapon_k98_scoped.cpp @@ -0,0 +1,156 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodsniper.h" + +#if defined( CLIENT_DLL ) + + #define CWeaponK98Scoped C_WeaponK98Scoped + +#endif + + +class CWeaponK98Scoped : public CDODSniperWeapon +{ +public: + DECLARE_CLASS( CWeaponK98Scoped, CDODSniperWeapon ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeaponK98Scoped() {} + + virtual void Spawn( void ); + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_K98_SCOPED; } + + // weapon id for stats purposes + virtual DODWeaponID GetStatsWeaponID( void ) + { + if ( IsFullyZoomed() ) + return WEAPON_K98_SCOPED_ZOOMED; + else + return WEAPON_K98_SCOPED; + } + + virtual bool ShouldAutoEjectBrass( void ) { return false; } + virtual bool ShouldDrawCrosshair( void ) { return false; } + + virtual bool ShouldDrawViewModel( void ) + { + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); + + Assert( pPlayer ); + + if ( !pPlayer ) + return false; + + if ( pPlayer->GetFOV() < 90 ) + return false; + + // handle case when we are spectating someone and don't know their fov + if ( IsFullyZoomed() ) + return false; + + return BaseClass::ShouldDrawViewModel(); + } + + virtual bool ShouldDrawMuzzleFlash( void ) + { + return ShouldDrawViewModel(); + } + + virtual Activity GetPrimaryAttackActivity( void ); + + virtual float GetFireDelay( void ); + + virtual float GetRecoil( void ) { return 6.0f; } + +private: + CWeaponK98Scoped( const CWeaponK98Scoped & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponK98Scoped, DT_WeaponK98Scoped ) + +BEGIN_NETWORK_TABLE( CWeaponK98Scoped, DT_WeaponK98Scoped ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponK98Scoped ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_k98_scoped, CWeaponK98Scoped ); +PRECACHE_WEAPON_REGISTER( weapon_k98_scoped ); + +void CWeaponK98Scoped::Spawn( void ) +{ + m_iAltFireHint = HINT_USE_ZOOM; + + BaseClass::Spawn(); +} + +acttable_t CWeaponK98Scoped::m_acttable[] = +{ + { ACT_DOD_STAND_AIM, ACT_DOD_STAND_AIM_BOLT, false }, + { ACT_DOD_CROUCH_AIM, ACT_DOD_CROUCH_AIM_BOLT, false }, + { ACT_DOD_CROUCHWALK_AIM, ACT_DOD_CROUCHWALK_AIM_BOLT, false }, + { ACT_DOD_WALK_AIM, ACT_DOD_WALK_AIM_BOLT, false }, + { ACT_DOD_RUN_AIM, ACT_DOD_RUN_AIM_BOLT, false }, + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_BOLT, false }, + { ACT_PRONE_FORWARD, ACT_DOD_PRONEWALK_IDLE_BOLT, false }, + { ACT_DOD_STAND_IDLE, ACT_DOD_STAND_IDLE_BOLT, false }, + { ACT_DOD_CROUCH_IDLE, ACT_DOD_CROUCH_IDLE_BOLT, false }, + { ACT_DOD_CROUCHWALK_IDLE, ACT_DOD_CROUCHWALK_IDLE_BOLT, false }, + { ACT_DOD_WALK_IDLE, ACT_DOD_WALK_IDLE_BOLT, false }, + { ACT_DOD_RUN_IDLE, ACT_DOD_RUN_IDLE_BOLT, false }, + { ACT_SPRINT, ACT_DOD_SPRINT_IDLE_BOLT, false }, + + // Zoomed Aim + { ACT_DOD_IDLE_ZOOMED, ACT_DOD_STAND_ZOOM_BOLT, false }, + { ACT_DOD_CROUCH_ZOOMED, ACT_DOD_CROUCH_ZOOM_BOLT, false }, + { ACT_DOD_CROUCHWALK_ZOOMED, ACT_DOD_CROUCHWALK_ZOOM_BOLT, false }, + { ACT_DOD_WALK_ZOOMED, ACT_DOD_WALK_ZOOM_BOLT, false }, + { ACT_DOD_PRONE_ZOOMED, ACT_DOD_PRONE_ZOOM_BOLT, false }, + { ACT_DOD_PRONE_FORWARD_ZOOMED, ACT_DOD_PRONE_ZOOM_FORWARD_BOLT, false }, + + // Attack ( prone? ) + { ACT_RANGE_ATTACK1, ACT_DOD_PRIMARYATTACK_BOLT, false }, + { ACT_DOD_PRIMARYATTACK_CROUCH, ACT_DOD_PRIMARYATTACK_BOLT, false }, + { ACT_DOD_PRIMARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_BOLT, false }, + + // Reload ( prone ? ) + { ACT_RELOAD, ACT_DOD_RELOAD_BOLT, false }, + { ACT_DOD_RELOAD_CROUCH, ACT_DOD_RELOAD_CROUCH_BOLT, false }, + { ACT_DOD_RELOAD_PRONE, ACT_DOD_RELOAD_PRONE_BOLT, false }, + + // Hand Signals + { ACT_DOD_HS_IDLE, ACT_DOD_HS_IDLE_K98, false }, + { ACT_DOD_HS_CROUCH, ACT_DOD_HS_CROUCH_K98, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponK98Scoped ); + +Activity CWeaponK98Scoped::GetPrimaryAttackActivity( void ) +{ + Activity actPrim; + + if( m_iClip1 <= 0 ) + actPrim = ACT_VM_PRIMARYATTACK_EMPTY; + else + actPrim = ACT_VM_PRIMARYATTACK; + + return actPrim; +} + +float CWeaponK98Scoped::GetFireDelay( void ) +{ + if ( m_iClip1 <= 0 ) + { + return SequenceDuration(); + } + + return BaseClass::GetFireDelay(); +}
\ No newline at end of file diff --git a/game/shared/dod/weapon_m1carbine.cpp b/game/shared/dod/weapon_m1carbine.cpp new file mode 100644 index 0000000..820d5a3 --- /dev/null +++ b/game/shared/dod/weapon_m1carbine.cpp @@ -0,0 +1,76 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodsemiauto.h" + +#if defined( CLIENT_DLL ) + + #define CWeaponM1Carbine C_WeaponM1Carbine + +#endif + + +class CWeaponM1Carbine : public CDODSemiAutoWeapon +{ +public: + DECLARE_CLASS( CWeaponM1Carbine, CDODSemiAutoWeapon ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeaponM1Carbine() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_M1CARBINE; } + + virtual float GetRecoil( void ) { return 1.4f; } + +private: + CWeaponM1Carbine( const CWeaponM1Carbine & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponM1Carbine, DT_WeaponM1Carbine ) + +BEGIN_NETWORK_TABLE( CWeaponM1Carbine, DT_WeaponM1Carbine ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponM1Carbine ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_m1carbine, CWeaponM1Carbine ); +PRECACHE_WEAPON_REGISTER( weapon_m1carbine ); + +acttable_t CWeaponM1Carbine::m_acttable[] = +{ + { ACT_DOD_STAND_AIM, ACT_DOD_STAND_AIM_RIFLE, false }, + { ACT_DOD_CROUCH_AIM, ACT_DOD_CROUCH_AIM_RIFLE, false }, + { ACT_DOD_CROUCHWALK_AIM, ACT_DOD_CROUCHWALK_AIM_RIFLE, false }, + { ACT_DOD_WALK_AIM, ACT_DOD_WALK_AIM_RIFLE, false }, + { ACT_DOD_RUN_AIM, ACT_DOD_RUN_AIM_RIFLE, false }, + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_RIFLE, false }, + { ACT_PRONE_FORWARD, ACT_DOD_PRONEWALK_IDLE_RIFLE, false }, + { ACT_DOD_STAND_IDLE, ACT_DOD_STAND_IDLE_RIFLE, false }, + { ACT_DOD_CROUCH_IDLE, ACT_DOD_CROUCH_IDLE_RIFLE, false }, + { ACT_DOD_CROUCHWALK_IDLE, ACT_DOD_CROUCHWALK_IDLE_RIFLE, false }, + { ACT_DOD_WALK_IDLE, ACT_DOD_WALK_IDLE_RIFLE, false }, + { ACT_DOD_RUN_IDLE, ACT_DOD_RUN_IDLE_RIFLE, false }, + { ACT_SPRINT, ACT_DOD_SPRINT_IDLE_RIFLE, false }, + + // Attack ( prone? ) + { ACT_RANGE_ATTACK1, ACT_DOD_PRIMARYATTACK_RIFLE, false }, + { ACT_DOD_PRIMARYATTACK_CROUCH, ACT_DOD_PRIMARYATTACK_RIFLE, false }, + { ACT_DOD_PRIMARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_RIFLE, false }, + + { ACT_RELOAD, ACT_DOD_RELOAD_M1CARBINE, false }, + { ACT_DOD_RELOAD_CROUCH, ACT_DOD_RELOAD_CROUCH_M1CARBINE, false }, + { ACT_DOD_RELOAD_PRONE, ACT_DOD_RELOAD_PRONE_M1CARBINE, false }, + + // Hand Signals + { ACT_DOD_HS_IDLE, ACT_DOD_HS_IDLE_K98, false }, + { ACT_DOD_HS_CROUCH, ACT_DOD_HS_CROUCH_K98, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponM1Carbine );
\ No newline at end of file diff --git a/game/shared/dod/weapon_mg34.cpp b/game/shared/dod/weapon_mg34.cpp new file mode 100644 index 0000000..3479485 --- /dev/null +++ b/game/shared/dod/weapon_mg34.cpp @@ -0,0 +1,207 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodbipodgun.h" + +#if defined( CLIENT_DLL ) + + #define CWeaponMG34 C_WeaponMG34 + #include "c_dod_player.h" + +#else + + #include "dod_player.h" +#endif + + +class CWeaponMG34 : public CDODBipodWeapon +{ +public: + DECLARE_CLASS( CWeaponMG34, CDODBipodWeapon ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeaponMG34() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_MG34; } + + virtual void PrimaryAttack( void ); + virtual bool Reload( void ); + + Activity GetReloadActivity( void ); + Activity GetDrawActivity( void ); + Activity GetDeployActivity( void ); + Activity GetUndeployActivity( void ); + Activity GetIdleActivity( void ); + Activity GetPrimaryAttackActivity( void ); + + virtual bool ShouldDrawCrosshair( void ) { return IsDeployed(); } + +private: + CWeaponMG34( const CWeaponMG34 & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponMG34, DT_WeaponMG34 ) + +BEGIN_NETWORK_TABLE( CWeaponMG34, DT_WeaponMG34 ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponMG34 ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_mg34, CWeaponMG34 ); +PRECACHE_WEAPON_REGISTER( weapon_mg34 ); + +acttable_t CWeaponMG34::m_acttable[] = +{ + // Aim + { ACT_IDLE, ACT_DOD_STAND_AIM_MG, false }, + { ACT_CROUCHIDLE, ACT_DOD_CROUCH_AIM_MG, false }, + { ACT_RUN_CROUCH, ACT_DOD_CROUCHWALK_AIM_MG, false }, + { ACT_WALK, ACT_DOD_WALK_AIM_MG, false }, + { ACT_RUN, ACT_DOD_RUN_AIM_MG, false }, + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_MG, false }, + + // Deployed Aim + { ACT_DOD_DEPLOYED, ACT_DOD_DEPLOY_MG, false }, + { ACT_DOD_PRONE_DEPLOYED, ACT_DOD_PRONE_DEPLOY_MG, false }, + + // Attack ( prone? deployed? ) + { ACT_RANGE_ATTACK1, ACT_DOD_PRIMARYATTACK_MG, false }, + { ACT_DOD_PRIMARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_MG, false }, + { ACT_DOD_PRIMARYATTACK_DEPLOYED, ACT_DOD_PRIMARYATTACK_DEPLOYED_MG, false }, + { ACT_DOD_PRIMARYATTACK_PRONE_DEPLOYED, ACT_DOD_PRIMARYATTACK_PRONE_DEPLOYED_MG,false }, + + // Reload ( prone? deployed? ) + { ACT_DOD_RELOAD_DEPLOYED, ACT_DOD_RELOAD_DEPLOYED_MG34, false }, + { ACT_DOD_RELOAD_PRONE_DEPLOYED, ACT_DOD_RELOAD_PRONE_DEPLOYED_MG34, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponMG34 ); + +void CWeaponMG34::PrimaryAttack( void ) +{ +#ifdef CLIENT_DLL + C_DODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); +#else + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); +#endif + + Assert( pPlayer ); + + if( !IsDeployed() ) + { +#ifdef CLIENT_DLL + pPlayer->HintMessage( HINT_MG_FIRE_UNDEPLOYED ); +#endif + pPlayer->m_Shared.SetSlowedTime( 0.2 ); + + float flStamina = pPlayer->m_Shared.GetStamina(); + + pPlayer->m_Shared.SetStamina( flStamina - 15 ); + } + + BaseClass::PrimaryAttack(); +} + +bool CWeaponMG34::Reload( void ) +{ + if( !IsDeployed() ) + { + ClientPrint( GetPlayerOwner(), HUD_PRINTCENTER, "#Dod_mg_reload" ); + return false; + } + + return BaseClass::Reload(); +} + +Activity CWeaponMG34::GetReloadActivity( void ) +{ + return ACT_VM_RELOAD; +} + +Activity CWeaponMG34::GetDrawActivity( void ) +{ + Activity actDraw; + + if( m_iClip1 <= 0 ) + actDraw = ACT_VM_DRAW_EMPTY; + else + actDraw = ACT_VM_DRAW; + + return actDraw; +} + +Activity CWeaponMG34::GetDeployActivity( void ) +{ + Activity actDeploy; + + if( m_iClip1 <= 0 ) + actDeploy = ACT_VM_DEPLOY_EMPTY; + else + actDeploy = ACT_VM_DEPLOY; + + return actDeploy; +} + +Activity CWeaponMG34::GetUndeployActivity( void ) +{ + Activity actUndeploy; + + if( m_iClip1 <= 0 ) + actUndeploy = ACT_VM_UNDEPLOY_EMPTY; + else + actUndeploy = ACT_VM_UNDEPLOY; + + return actUndeploy; +} + +Activity CWeaponMG34::GetIdleActivity( void ) +{ + Activity actIdle; + + if( IsDeployed() ) + { + if( m_iClip1 <= 0 ) + actIdle = ACT_VM_IDLE_DEPLOYED_EMPTY; + else + actIdle = ACT_VM_IDLE_DEPLOYED; + } + else + { + if( m_iClip1 <= 0 ) + actIdle = ACT_VM_IDLE_EMPTY; + else + actIdle = ACT_VM_IDLE; + } + + return actIdle; +} + +Activity CWeaponMG34::GetPrimaryAttackActivity( void ) +{ + Activity actPrim; + + if( IsDeployed() ) + { + if( m_iClip1 <= 0 ) + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED_EMPTY; + else + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED; + } + else + { + if( m_iClip1 <= 0 ) + actPrim = ACT_VM_PRIMARYATTACK_EMPTY; + else + actPrim = ACT_VM_PRIMARYATTACK; + } + + return actPrim; +} + diff --git a/game/shared/dod/weapon_mg42.cpp b/game/shared/dod/weapon_mg42.cpp new file mode 100644 index 0000000..7321476 --- /dev/null +++ b/game/shared/dod/weapon_mg42.cpp @@ -0,0 +1,843 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_mg42.h" +#include "engine/ivdebugoverlay.h" + +#if defined( CLIENT_DLL ) + + #include "tier1/KeyValues.h" + #include "particles_simple.h" + #include "particles_localspace.h" + #include "fx.h" + #include "c_dod_player.h" + +#else + + #include "dod_player.h" + +#endif + + +#ifdef CLIENT_DLL +void ToolFramework_PostToolMessage( HTOOLHANDLE hEntity, KeyValues *msg ); +#endif + + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponMG42, DT_WeaponMG42 ) + +#ifdef GAME_DLL + +BEGIN_DATADESC( CWeaponMG42 ) + DEFINE_THINKFUNC( CoolThink ), +END_DATADESC() + +#endif + +BEGIN_NETWORK_TABLE( CWeaponMG42, DT_WeaponMG42 ) +#ifdef CLIENT_DLL + RecvPropInt ( RECVINFO( m_iWeaponHeat ) ), + RecvPropTime ( RECVINFO( m_flNextCoolTime ) ), + RecvPropBool ( RECVINFO( m_bOverheated ) ), +#else + SendPropInt ( SENDINFO( m_iWeaponHeat ), 7, SPROP_UNSIGNED ), + SendPropFloat ( SENDINFO( m_flNextCoolTime ) ), + SendPropBool ( SENDINFO( m_bOverheated ) ), +#endif +END_NETWORK_TABLE() + +#ifdef CLIENT_DLL +BEGIN_PREDICTION_DATA( CWeaponMG42 ) + DEFINE_PRED_FIELD( m_iWeaponHeat, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD_TOL( m_flNextCoolTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ), + DEFINE_PRED_FIELD( m_bOverheated, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), +END_PREDICTION_DATA() +#endif + +LINK_ENTITY_TO_CLASS( weapon_mg42, CWeaponMG42 ); +PRECACHE_WEAPON_REGISTER( weapon_mg42 ); + +acttable_t CWeaponMG42::m_acttable[] = +{ + { ACT_DOD_STAND_AIM, ACT_DOD_STAND_AIM_MG, false }, + { ACT_DOD_CROUCH_AIM, ACT_DOD_CROUCH_AIM_MG, false }, + { ACT_DOD_CROUCHWALK_AIM, ACT_DOD_CROUCHWALK_AIM_MG, false }, + { ACT_DOD_WALK_AIM, ACT_DOD_WALK_AIM_MG, false }, + { ACT_DOD_RUN_AIM, ACT_DOD_RUN_AIM_MG, false }, + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_MG, false }, + { ACT_PRONE_FORWARD, ACT_DOD_PRONEWALK_IDLE_MG, false }, + { ACT_DOD_STAND_IDLE, ACT_DOD_STAND_IDLE_MG, false }, + { ACT_DOD_CROUCH_IDLE, ACT_DOD_CROUCH_IDLE_MG, false }, + { ACT_DOD_CROUCHWALK_IDLE, ACT_DOD_CROUCHWALK_IDLE_MG, false }, + { ACT_DOD_WALK_IDLE, ACT_DOD_WALK_IDLE_MG, false }, + { ACT_DOD_RUN_IDLE, ACT_DOD_RUN_IDLE_MG, false }, + { ACT_SPRINT, ACT_DOD_SPRINT_IDLE_MG, false }, + + // Deployed Aim + { ACT_DOD_DEPLOYED, ACT_DOD_DEPLOY_MG, false }, + { ACT_DOD_PRONE_DEPLOYED, ACT_DOD_PRONE_DEPLOY_MG, false }, + + // Attack ( prone? deployed? ) + { ACT_RANGE_ATTACK1, ACT_DOD_PRIMARYATTACK_MG, false }, + { ACT_DOD_PRIMARYATTACK_CROUCH, ACT_DOD_PRIMARYATTACK_MG, false }, + { ACT_DOD_PRIMARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_MG, false }, + { ACT_DOD_PRIMARYATTACK_DEPLOYED, ACT_DOD_PRIMARYATTACK_DEPLOYED_MG, false }, + { ACT_DOD_PRIMARYATTACK_PRONE_DEPLOYED, ACT_DOD_PRIMARYATTACK_PRONE_DEPLOYED_MG,false }, + + // Reload ( prone? deployed? ) + { ACT_DOD_RELOAD_DEPLOYED, ACT_DOD_RELOAD_DEPLOYED_MG, false }, + { ACT_DOD_RELOAD_PRONE_DEPLOYED, ACT_DOD_RELOAD_PRONE_DEPLOYED_MG, false }, + + // Hand Signals + { ACT_DOD_HS_IDLE, ACT_DOD_HS_IDLE_MG42, false }, + { ACT_DOD_HS_CROUCH, ACT_DOD_HS_CROUCH_MG42, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponMG42 ); + +void CWeaponMG42::Spawn( void ) +{ + m_iWeaponHeat = 0; + m_flNextCoolTime = 0; + + m_bOverheated = false; + +#ifdef CLIENT_DLL + m_pEmitter = NULL; + m_flParticleAccumulator = 0.0; + m_hParticleMaterial = ParticleMgr()->GetPMaterial( "sprites/effects/bazookapuff" ); +#endif + + BaseClass::Spawn(); +} + +#ifdef CLIENT_DLL + + CWeaponMG42::~CWeaponMG42() + { + if ( clienttools->IsInRecordingMode() && m_pEmitter.IsValid() && m_pEmitter->GetToolParticleEffectId() != TOOLPARTICLESYSTEMID_INVALID ) + { + KeyValues *msg = new KeyValues( "ParticleSystem_ActivateEmitter" ); + msg->SetInt( "id", m_pEmitter->GetToolParticleEffectId() ); + msg->SetFloat( "time", gpGlobals->curtime ); + msg->SetInt( "active", 0 ); + + msg->SetInt( "emitter", 0 ); + ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg ); + + msg->SetInt( "emitter", 1 ); + ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg ); + + msg->SetInt( "emitter", 2 ); + ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg ); + + msg->SetInt( "emitter", 3 ); + ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg ); + + msg->deleteThis(); + } + } + + void CWeaponMG42::OnDataChanged( DataUpdateType_t updateType ) + { + BaseClass::OnDataChanged( updateType ); + + // BUG! This can happen more than once! + if ( updateType == DATA_UPDATE_CREATED ) + { + if ( !m_pEmitter.IsValid() ) + { + m_pEmitter = CSimpleEmitter::Create( "MGOverheat" ); + } + + Assert( m_pEmitter.IsValid() ); + } + + ClientThinkList()->SetNextClientThink( GetClientHandle(), CLIENT_THINK_ALWAYS ); + } + + // Client Think emits smoke particles based on heat + // ( except if we are holstered ) + void CWeaponMG42::ClientThink( void ) + { + m_pEmitter->SetSortOrigin( GetAbsOrigin() ); + + float flEmitRate = 0.0; //particles per second + + // Only smoke if we are dropped ( no owner ) or if we have an owner and are active + if ( GetOwner() == NULL || GetOwner()->GetActiveWeapon() == this ) + { + if ( m_iWeaponHeat > 85 ) + { + flEmitRate = 30; + } + else if ( m_iWeaponHeat > 80 ) + { + flEmitRate = 20; + } + else if ( m_iWeaponHeat > 65 ) + { + flEmitRate = 10; + } + else if ( m_iWeaponHeat > 50 ) + { + flEmitRate = 5; + } + } + + m_flParticleAccumulator += ( gpGlobals->frametime * flEmitRate ); + + while( m_flParticleAccumulator > 0.0 ) + { + EmitSmokeParticle(); + + m_flParticleAccumulator -= 1.0; + } + } + + void CWeaponMG42::EmitSmokeParticle( void ) + { + Vector vFront, vBack; + QAngle angles; + + C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); + + bool bViewModel = false; + + // if this is locally owned + if ( GetPlayerOwner() == pLocalPlayer ) + { + C_BaseViewModel *vm = pLocalPlayer->GetViewModel( 0 ); + + if ( !vm ) + return; + + vm->GetAttachment( 1, vFront, angles ); + vm->GetAttachment( 2, vBack, angles ); + + bViewModel = true; + } + else + { + // could be dropped, or held by another player + GetAttachment( 1, vFront, angles ); + GetAttachment( 2, vBack, angles ); + } + + // Get a position somewhere on the barrel + Vector vPos = vBack + random->RandomFloat(0.0, 1.0 ) * ( vFront - vBack ); + + SimpleParticle *pParticle = m_pEmitter->AddSimpleParticle( m_hParticleMaterial, vPos ); + if ( pParticle ) + { + pParticle->m_vecVelocity = Vector( 0,0,12 ); + pParticle->m_flRoll = random->RandomFloat( 0, 0.5 ); + pParticle->m_flRollDelta = ( random->RandomInt(0,1) == 0 ? 1 : -1 ) * random->RandomFloat( 0.5, 1.0 ); + pParticle->m_flDieTime = 1.8f; + pParticle->m_flLifetime = 0; + pParticle->m_uchColor[0] = 200; + pParticle->m_uchColor[1] = 200; + pParticle->m_uchColor[2] = 200; + pParticle->m_uchStartAlpha = 60; + pParticle->m_uchEndAlpha = 0; + pParticle->m_uchStartSize = 4; + pParticle->m_uchEndSize = 25; + pParticle->m_iFlags = 0; //bViewModel ? FLE_VIEWMODEL : 0; + } + } + +#else + + // This function does the cooling when the weapon is dropped or holstered + // regular, predicted cooling is done in ItemPostFrame + void CWeaponMG42::CoolThink( void ) + { + if ( m_iWeaponHeat > 0 ) + m_iWeaponHeat--; + + SetContextThink( &CWeaponMG42::CoolThink, gpGlobals->curtime + 0.1, COOL_CONTEXT ); + } + +#endif + +void CWeaponMG42::Precache() +{ + PrecacheMaterial( "sprites/effects/bazookapuff" ); + + PrecacheScriptSound( "Weapon_Mg42.OverHeat" ); + + BaseClass::Precache(); +} + +bool CWeaponMG42::Reload( void ) +{ + if( !IsDeployed() ) + { +#ifdef CLIENT_DLL + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + + if ( pPlayer ) + pPlayer->HintMessage( HINT_MG_DEPLOY_TO_RELOAD ); +#endif + return false; + } + + return BaseClass::Reload(); +} + +void CWeaponMG42::FinishReload( void ) +{ + BaseClass::FinishReload(); + + //Reset the heat when you complete a reload + m_iWeaponHeat = 0; +} + +void CWeaponMG42::PrimaryAttack( void ) +{ + if( m_bOverheated ) + { + return; + } + + if ( m_iClip1 <= 0 ) + { + if (m_bFireOnEmpty) + { + PlayEmptySound(); + m_flNextPrimaryAttack = gpGlobals->curtime + 0.2; + } + + return; + } + + + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + Assert( pPlayer ); + + if( m_iWeaponHeat >= 99 ) + { + //can't fire anymore, wait until heat is below 80 +#ifdef CLIENT_DLL + pPlayer->HintMessage( HINT_WEAPON_OVERHEAT ); +#endif + m_bOverheated = true; + m_bInAttack = true; + + EmitSound( "Weapon_Mg42.OverHeat" ); + return; + } + + m_iWeaponHeat += 1; //2; + m_flNextCoolTime = gpGlobals->curtime + 0.16f; + + if( !IsDeployed() ) + { +#ifdef CLIENT_DLL + pPlayer->HintMessage( HINT_MG_FIRE_UNDEPLOYED ); +#endif + pPlayer->m_Shared.SetSlowedTime( 0.2 ); + + float flStamina = pPlayer->m_Shared.GetStamina(); + + pPlayer->m_Shared.SetStamina( flStamina - 15 ); + } + + BaseClass::PrimaryAttack(); +} + +void CWeaponMG42::ItemPostFrame( void ) +{ + ItemFrameCool(); + + if( m_iWeaponHeat < 80 ) + m_bOverheated = false; + + BaseClass::ItemPostFrame(); +} + +void CWeaponMG42::ItemBusyFrame( void ) +{ + ItemFrameCool(); + + BaseClass::ItemBusyFrame(); +} + +void CWeaponMG42::ItemFrameCool( void ) +{ + if( gpGlobals->curtime > m_flNextCoolTime ) + { + if ( m_iWeaponHeat > 0 ) + m_iWeaponHeat--; + + m_flNextCoolTime = gpGlobals->curtime + 0.16f; + } +} + +bool CWeaponMG42::Deploy( void ) +{ + // stop the fake cooling when we deploy the weapon + SetContextThink( NULL, 0.0, COOL_CONTEXT ); + + return BaseClass::Deploy(); +} + +bool CWeaponMG42::Holster( CBaseCombatWeapon *pSwitchingTo ) +{ +#ifndef CLIENT_DLL + SetContextThink( &CWeaponMG42::CoolThink, gpGlobals->curtime + 0.1, COOL_CONTEXT ); +#endif + + return BaseClass::Holster(pSwitchingTo); +} + +void CWeaponMG42::Drop( const Vector &vecVelocity ) +{ +#ifndef CLIENT_DLL + SetContextThink( &CWeaponMG42::CoolThink, gpGlobals->curtime + 0.1, COOL_CONTEXT ); +#endif + + BaseClass::Drop( vecVelocity ); +} + +Activity CWeaponMG42::GetReloadActivity( void ) +{ + return ACT_VM_RELOAD; +} + +Activity CWeaponMG42::GetDrawActivity( void ) +{ + Activity actDraw; + + if( 0 && m_iClip1 <= 0 ) + actDraw = ACT_VM_DRAW_EMPTY; + else + actDraw = ACT_VM_DRAW; + + return actDraw; +} + +Activity CWeaponMG42::GetDeployActivity( void ) +{ + Activity actDeploy; + + switch ( m_iClip1 ) + { + case 8: + actDeploy = ACT_VM_DEPLOY_8; + break; + case 7: + actDeploy = ACT_VM_DEPLOY_7; + break; + case 6: + actDeploy = ACT_VM_DEPLOY_6; + break; + case 5: + actDeploy = ACT_VM_DEPLOY_5; + break; + case 4: + actDeploy = ACT_VM_DEPLOY_4; + break; + case 3: + actDeploy = ACT_VM_DEPLOY_3; + break; + case 2: + actDeploy = ACT_VM_DEPLOY_2; + break; + case 1: + actDeploy = ACT_VM_DEPLOY_1; + break; + case 0: + actDeploy = ACT_VM_DEPLOY_EMPTY; + break; + default: + actDeploy = ACT_VM_DEPLOY; + break; + } + + return actDeploy; +} + +Activity CWeaponMG42::GetUndeployActivity( void ) +{ + Activity actUndeploy; + + switch ( m_iClip1 ) + { + case 8: + actUndeploy = ACT_VM_UNDEPLOY_8; + break; + case 7: + actUndeploy = ACT_VM_UNDEPLOY_7; + break; + case 6: + actUndeploy = ACT_VM_UNDEPLOY_6; + break; + case 5: + actUndeploy = ACT_VM_UNDEPLOY_5; + break; + case 4: + actUndeploy = ACT_VM_UNDEPLOY_4; + break; + case 3: + actUndeploy = ACT_VM_UNDEPLOY_3; + break; + case 2: + actUndeploy = ACT_VM_UNDEPLOY_2; + break; + case 1: + actUndeploy = ACT_VM_UNDEPLOY_1; + break; + case 0: + actUndeploy = ACT_VM_UNDEPLOY_EMPTY; + break; + default: + actUndeploy = ACT_VM_UNDEPLOY; + break; + } + + return actUndeploy; +} + +Activity CWeaponMG42::GetIdleActivity( void ) +{ + Activity actIdle; + + if( IsDeployed() ) + { + switch ( m_iClip1 ) + { + case 8: + actIdle = ACT_VM_IDLE_DEPLOYED_8; + break; + case 7: + actIdle = ACT_VM_IDLE_DEPLOYED_7; + break; + case 6: + actIdle = ACT_VM_IDLE_DEPLOYED_6; + break; + case 5: + actIdle = ACT_VM_IDLE_DEPLOYED_5; + break; + case 4: + actIdle = ACT_VM_IDLE_DEPLOYED_4; + break; + case 3: + actIdle = ACT_VM_IDLE_DEPLOYED_3; + break; + case 2: + actIdle = ACT_VM_IDLE_DEPLOYED_2; + break; + case 1: + actIdle = ACT_VM_IDLE_DEPLOYED_1; + break; + case 0: + actIdle = ACT_VM_IDLE_DEPLOYED_EMPTY; + break; + default: + actIdle = ACT_VM_IDLE_DEPLOYED; + break; + } + } + else + { + switch ( m_iClip1 ) + { + case 8: + actIdle = ACT_VM_IDLE_8; + break; + case 7: + actIdle = ACT_VM_IDLE_7; + break; + case 6: + actIdle = ACT_VM_IDLE_6; + break; + case 5: + actIdle = ACT_VM_IDLE_5; + break; + case 4: + actIdle = ACT_VM_IDLE_4; + break; + case 3: + actIdle = ACT_VM_IDLE_3; + break; + case 2: + actIdle = ACT_VM_IDLE_2; + break; + case 1: + actIdle = ACT_VM_IDLE_1; + break; + case 0: + actIdle = ACT_VM_IDLE_EMPTY; + break; + default: + actIdle = ACT_VM_IDLE; + break; + } + } + + return actIdle; +} + +Activity CWeaponMG42::GetPrimaryAttackActivity( void ) +{ + Activity actPrim; + + if( IsDeployed() ) + { + switch ( m_iClip1 ) + { + case 8: + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED_8; + break; + case 7: + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED_7; + break; + case 6: + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED_6; + break; + case 5: + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED_5; + break; + case 4: + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED_4; + break; + case 3: + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED_3; + break; + case 2: + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED_2; + break; + case 1: + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED_1; + break; + case 0: + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED_EMPTY; + break; + default: + actPrim = ACT_VM_PRIMARYATTACK_DEPLOYED; + break; + } + } + else + { + switch ( m_iClip1 ) + { + case 8: + actPrim = ACT_VM_PRIMARYATTACK_8; + break; + case 7: + actPrim = ACT_VM_PRIMARYATTACK_7; + break; + case 6: + actPrim = ACT_VM_PRIMARYATTACK_6; + break; + case 5: + actPrim = ACT_VM_PRIMARYATTACK_5; + break; + case 4: + actPrim = ACT_VM_PRIMARYATTACK_4; + break; + case 3: + actPrim = ACT_VM_PRIMARYATTACK_3; + break; + case 2: + actPrim = ACT_VM_PRIMARYATTACK_2; + break; + case 1: + actPrim = ACT_VM_PRIMARYATTACK_1; + break; + case 0: + actPrim = ACT_VM_PRIMARYATTACK_EMPTY; + break; + default: + actPrim = ACT_VM_PRIMARYATTACK; + break; + } + } + + return actPrim; +} + +float CWeaponMG42::GetRecoil( void ) +{ + CDODPlayer *p = ToDODPlayer( GetPlayerOwner() ); + + if( p && p->m_Shared.IsInMGDeploy() ) + { + return 0.0f; + } + + return 20; +} + +#ifdef CLIENT_DLL + +//----------------------------------------------------------------------------- +// This is called after sending this entity's recording state +//----------------------------------------------------------------------------- +void CWeaponMG42::CleanupToolRecordingState( KeyValues *msg ) +{ + BaseClass::CleanupToolRecordingState( msg ); + + // Generally, this is used to allow the entity to clean up + // allocated state it put into the message, but here we're going + // to use it to send particle system messages because we + // know the smoke has been recorded at this point + if ( !clienttools->IsInRecordingMode() || !m_pEmitter.IsValid() ) + return; + + // NOTE: Particle system destruction message will be sent by the particle effect itself. + if ( m_pEmitter->GetToolParticleEffectId() == TOOLPARTICLESYSTEMID_INVALID ) + { + int nId = m_pEmitter->AllocateToolParticleEffectId(); + + KeyValues *msg = new KeyValues( "ParticleSystem_Create" ); + msg->SetString( "name", "CWeaponMG42 smoke" ); + msg->SetInt( "id", nId ); + msg->SetFloat( "time", gpGlobals->curtime ); + + KeyValues *pEmitter0 = msg->FindKey( "DmeSpriteEmitter", true ); + pEmitter0->SetInt( "count", 5 ); // particles per second, when duration is < 0 + pEmitter0->SetFloat( "duration", -1 ); + pEmitter0->SetString( "material", "sprites/effects/bazookapuff" ); + pEmitter0->SetInt( "active", 0 ); + + KeyValues *pInitializers = pEmitter0->FindKey( "initializers", true ); + + KeyValues *pPosition = pInitializers->FindKey( "DmeRandomAttachmentPositionEntityInitializer", true ); + pPosition->SetPtr( "entindex", (void*)entindex() ); + pPosition->SetInt( "attachmentIndex0", 1 ); + pPosition->SetInt( "attachmentIndex1", 2 ); + + KeyValues *pLifetime = pInitializers->FindKey( "DmeRandomLifetimeInitializer", true ); + pLifetime->SetFloat( "minLifetime", 1.8f ); + pLifetime->SetFloat( "maxLifetime", 1.8f ); + + KeyValues *pVelocity = pInitializers->FindKey( "DmeConstantVelocityInitializer", true ); + pVelocity->SetFloat( "velocityX", 0.0f ); + pVelocity->SetFloat( "velocityY", 0.0f ); + pVelocity->SetFloat( "velocityZ", 12.0f ); + + KeyValues *pRoll = pInitializers->FindKey( "DmeRandomRollInitializer", true ); + pRoll->SetFloat( "minRoll", 0.0f ); + pRoll->SetFloat( "maxRoll", 0.5f ); + + KeyValues *pRollSpeed = pInitializers->FindKey( "DmeSplitRandomRollSpeedInitializer", true ); + pRollSpeed->SetFloat( "minRollSpeed", 0.5f ); + pRollSpeed->SetFloat( "maxRollSpeed", 1.0f ); + + KeyValues *pColor = pInitializers->FindKey( "DmeRandomInterpolatedColorInitializer", true ); + pColor->SetColor( "color1", Color( 200, 200, 200, 255 ) ); + pColor->SetColor( "color2", Color( 200, 200, 200, 255 ) ); + + KeyValues *pAlpha = pInitializers->FindKey( "DmeRandomAlphaInitializer", true ); + pAlpha->SetInt( "minStartAlpha", 60 ); + pAlpha->SetInt( "maxStartAlpha", 60 ); + pAlpha->SetInt( "minEndAlpha", 0 ); + pAlpha->SetInt( "maxEndAlpha", 0 ); + + KeyValues *pSize = pInitializers->FindKey( "DmeRandomSizeInitializer", true ); + pSize->SetFloat( "minStartSize", 4 ); + pSize->SetFloat( "maxStartSize", 4 ); + pSize->SetFloat( "minEndSize", 25 ); + pSize->SetFloat( "maxEndSize", 25 ); + + KeyValues *pUpdaters = pEmitter0->FindKey( "updaters", true ); + + pUpdaters->FindKey( "DmePositionVelocityUpdater", true ); + pUpdaters->FindKey( "DmeRollUpdater", true ); + pUpdaters->FindKey( "DmeAlphaLinearUpdater", true ); + pUpdaters->FindKey( "DmeSizeUpdater", true ); + + // create emitters for each emission rate: 5,10,20,30 + KeyValues *pEmitter1 = pEmitter0->MakeCopy(); + pEmitter1->SetInt( "count", 10 ); + msg->AddSubKey( pEmitter1 ); + + KeyValues *pEmitter2 = pEmitter0->MakeCopy(); + pEmitter2->SetInt( "count", 20 ); + msg->AddSubKey( pEmitter2 ); + + KeyValues *pEmitter3 = pEmitter0->MakeCopy(); + pEmitter3->SetInt( "count", 30 ); + msg->AddSubKey( pEmitter3 ); + + // mark only the appropriate emitter active + bool bHolstered = GetOwner() && GetOwner()->GetActiveWeapon() != this; + if ( !bHolstered ) + { + if ( m_iWeaponHeat > 85 ) + { + pEmitter3->SetInt( "active", 1 ); + } + else if ( m_iWeaponHeat > 80 ) + { + pEmitter2->SetInt( "active", 1 ); + } + else if ( m_iWeaponHeat > 65 ) + { + pEmitter1->SetInt( "active", 1 ); + } + else if ( m_iWeaponHeat > 50 ) + { + pEmitter0->SetInt( "active", 1 ); + } + } + + ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg ); + msg->deleteThis(); + } + else + { + int nEmitterIndex = -1; + bool bHolstered = GetOwner() && GetOwner()->GetActiveWeapon() != this; + if ( !bHolstered ) + { + if ( m_iWeaponHeat > 85 ) + { + nEmitterIndex = 3; + } + else if ( m_iWeaponHeat > 80 ) + { + nEmitterIndex = 2; + } + else if ( m_iWeaponHeat > 65 ) + { + nEmitterIndex = 1; + } + else if ( m_iWeaponHeat > 50 ) + { + nEmitterIndex = 0; + } + } + + KeyValues *msg = new KeyValues( "ParticleSystem_ActivateEmitter" ); + msg->SetInt( "id", m_pEmitter->GetToolParticleEffectId() ); + msg->SetFloat( "time", gpGlobals->curtime ); + + msg->SetInt( "emitter", 0 ); + msg->SetInt( "active", nEmitterIndex == 0 ); + ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg ); + + msg->SetInt( "emitter", 1 ); + msg->SetInt( "active", nEmitterIndex == 1 ); + ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg ); + + msg->SetInt( "emitter", 2 ); + msg->SetInt( "active", nEmitterIndex == 2 ); + ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg ); + + msg->SetInt( "emitter", 3 ); + msg->SetInt( "active", nEmitterIndex == 3 ); + ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg ); + + msg->deleteThis(); + } +} + +#endif diff --git a/game/shared/dod/weapon_mg42.h b/game/shared/dod/weapon_mg42.h new file mode 100644 index 0000000..1efe467 --- /dev/null +++ b/game/shared/dod/weapon_mg42.h @@ -0,0 +1,108 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef WEAPON_MG42_H +#define WEAPON_MG42_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "weapon_dodbase.h" +#include "weapon_dodbipodgun.h" + +#if defined( CLIENT_DLL ) + #define CWeaponMG42 C_WeaponMG42 +#endif + + +#define COOL_CONTEXT "CoolContext" + +class CWeaponMG42 : public CDODBipodWeapon +{ +public: + DECLARE_CLASS( CWeaponMG42, CDODBipodWeapon ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + +#ifdef GAME_DLL + DECLARE_DATADESC(); +#endif + + CWeaponMG42() {} +#ifdef CLIENT_DLL + virtual ~CWeaponMG42(); +#endif + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_MG42; } + + // weapon id for stats purposes + virtual DODWeaponID GetStatsWeaponID( void ) + { + if ( !IsDeployed() ) + return WEAPON_MG42_UNDEPLOYED; + else + return WEAPON_MG42; + } + + virtual void Spawn(); + virtual void Precache(); + virtual void PrimaryAttack( void ); + virtual bool Reload( void ); + virtual void FinishReload( void ); + virtual void ItemPostFrame( void ); + virtual void ItemBusyFrame( void ); + void ItemFrameCool( void ); + virtual bool Deploy( void ); + virtual bool Holster( CBaseCombatWeapon *pSwitchingTo ); + virtual void Drop( const Vector &vecVelocity ); + + virtual Activity GetReloadActivity( void ); + virtual Activity GetDrawActivity( void ); + virtual Activity GetDeployActivity( void ); + virtual Activity GetUndeployActivity( void ); + virtual Activity GetIdleActivity( void ); + virtual Activity GetPrimaryAttackActivity( void ); + + int GetWeaponHeat( void ) { return m_iWeaponHeat; } + + virtual bool ShouldDrawCrosshair( void ) { return IsDeployed(); } + + void Cool( void ); + +#ifdef CLIENT_DLL + virtual void ClientThink( void ); + virtual void OnDataChanged( DataUpdateType_t updateType ); + virtual void CleanupToolRecordingState( KeyValues *msg ); + + void EmitSmokeParticle( void ); +#else + void CoolThink( void ); +#endif + + virtual float GetRecoil( void ); + +private: + CWeaponMG42( const CWeaponMG42 & ); + + CNetworkVar( int, m_iWeaponHeat ); + CNetworkVar( float, m_flNextCoolTime ); + + CNetworkVar( bool, m_bOverheated ); + +#ifdef CLIENT_DLL + + CSmartPtr<CSimpleEmitter> m_pEmitter; + + float m_flParticleAccumulator; + PMaterialHandle m_hParticleMaterial; + +#endif + +}; + +#endif //WEAPON_MG42_H
\ No newline at end of file diff --git a/game/shared/dod/weapon_mp40.cpp b/game/shared/dod/weapon_mp40.cpp new file mode 100644 index 0000000..ba1f23f --- /dev/null +++ b/game/shared/dod/weapon_mp40.cpp @@ -0,0 +1,94 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodfullauto_punch.h" + + +#if defined( CLIENT_DLL ) + + #define CWeaponMP40 C_WeaponMP40 + +#endif + + +class CWeaponMP40 : public CDODFullAutoPunchWeapon +{ +public: + DECLARE_CLASS( CWeaponMP40, CDODFullAutoPunchWeapon ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeaponMP40() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_MP40; } + virtual DODWeaponID GetAltWeaponID( void ) const { return WEAPON_MP40_PUNCH; } + + virtual Activity GetIdleActivity( void ); + + virtual float GetRecoil( void ) { return 2.2f; } + +private: + CWeaponMP40( const CWeaponMP40 & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponMP40, DT_WeaponMP40 ) + +BEGIN_NETWORK_TABLE( CWeaponMP40, DT_WeaponMP40 ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponMP40 ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_mp40, CWeaponMP40 ); +PRECACHE_WEAPON_REGISTER( weapon_mp40 ); + +acttable_t CWeaponMP40::m_acttable[] = +{ + { ACT_DOD_STAND_AIM, ACT_DOD_STAND_AIM_MP40, false }, + { ACT_DOD_CROUCH_AIM, ACT_DOD_CROUCH_AIM_MP40, false }, + { ACT_DOD_CROUCHWALK_AIM, ACT_DOD_CROUCHWALK_AIM_MP40, false }, + { ACT_DOD_WALK_AIM, ACT_DOD_WALK_AIM_MP40, false }, + { ACT_DOD_RUN_AIM, ACT_DOD_RUN_AIM_MP40, false }, + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_MP40, false }, + { ACT_PRONE_FORWARD, ACT_DOD_PRONEWALK_IDLE_MP40, false }, + { ACT_DOD_STAND_IDLE, ACT_DOD_STAND_IDLE_MP40, false }, + { ACT_DOD_CROUCH_IDLE, ACT_DOD_CROUCH_IDLE_MP40, false }, + { ACT_DOD_CROUCHWALK_IDLE, ACT_DOD_CROUCHWALK_IDLE_MP40, false }, + { ACT_DOD_WALK_IDLE, ACT_DOD_WALK_IDLE_MP40, false }, + { ACT_DOD_RUN_IDLE, ACT_DOD_RUN_IDLE_MP40, false }, + { ACT_SPRINT, ACT_DOD_SPRINT_IDLE_MP40, false }, + + { ACT_RANGE_ATTACK1, ACT_DOD_PRIMARYATTACK_MP40, false }, + { ACT_DOD_PRIMARYATTACK_CROUCH, ACT_DOD_PRIMARYATTACK_MP40, false }, + { ACT_DOD_PRIMARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_MP40, false }, + { ACT_RANGE_ATTACK2, ACT_DOD_SECONDARYATTACK_MP40, false }, + { ACT_DOD_SECONDARYATTACK_CROUCH, ACT_DOD_SECONDARYATTACK_CROUCH_MP40,false }, + { ACT_DOD_SECONDARYATTACK_PRONE, ACT_DOD_SECONDARYATTACK_PRONE_MP40, false }, + + { ACT_RELOAD, ACT_DOD_RELOAD_MP40, false }, + { ACT_DOD_RELOAD_CROUCH, ACT_DOD_RELOAD_CROUCH_MP40, false }, + { ACT_DOD_RELOAD_PRONE, ACT_DOD_RELOAD_PRONE_MP40, false }, + + // Hand Signals + { ACT_DOD_HS_IDLE, ACT_DOD_HS_IDLE_MP44, false }, + { ACT_DOD_HS_CROUCH, ACT_DOD_HS_CROUCH_MP44, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponMP40 ); + +Activity CWeaponMP40::GetIdleActivity( void ) +{ + Activity actIdle; + + if( m_iClip1 < GetMaxClip1() ) + actIdle = ACT_VM_IDLE_EMPTY; + else + actIdle = ACT_VM_IDLE; + + return actIdle; +} diff --git a/game/shared/dod/weapon_mp44.cpp b/game/shared/dod/weapon_mp44.cpp new file mode 100644 index 0000000..1c2ccad --- /dev/null +++ b/game/shared/dod/weapon_mp44.cpp @@ -0,0 +1,87 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodfireselect.h" + + +#if defined( CLIENT_DLL ) + + #define CWeaponMP44 C_WeaponMP44 + +#endif + + +class CWeaponMP44 : public CDODFireSelectWeapon +{ +public: + DECLARE_CLASS( CWeaponMP44, CDODFireSelectWeapon ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeaponMP44() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_MP44; } + + // weapon id for stats purposes + virtual DODWeaponID GetStatsWeaponID( void ) + { + if ( IsSemiAuto() ) + return WEAPON_MP44_SEMIAUTO; + else + return WEAPON_MP44; + } + + virtual float GetRecoil( void ) { return 5.0f; } + +private: + CWeaponMP44( const CWeaponMP44 & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponMP44, DT_WeaponMP44 ) + +BEGIN_NETWORK_TABLE( CWeaponMP44, DT_WeaponMP44 ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponMP44 ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_mp44, CWeaponMP44 ); +PRECACHE_WEAPON_REGISTER( weapon_mp44 ); + +acttable_t CWeaponMP44::m_acttable[] = +{ + { ACT_DOD_STAND_AIM, ACT_DOD_STAND_AIM_MP44, false }, + { ACT_DOD_CROUCH_AIM, ACT_DOD_CROUCH_AIM_MP44, false }, + { ACT_DOD_CROUCHWALK_AIM, ACT_DOD_CROUCHWALK_AIM_MP44, false }, + { ACT_DOD_WALK_AIM, ACT_DOD_WALK_AIM_MP44, false }, + { ACT_DOD_RUN_AIM, ACT_DOD_RUN_AIM_MP44, false }, + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_MP44, false }, + { ACT_PRONE_FORWARD, ACT_DOD_PRONEWALK_IDLE_MP44, false }, + { ACT_DOD_STAND_IDLE, ACT_DOD_STAND_IDLE_MP44, false }, + { ACT_DOD_CROUCH_IDLE, ACT_DOD_CROUCH_IDLE_MP44, false }, + { ACT_DOD_CROUCHWALK_IDLE, ACT_DOD_CROUCHWALK_IDLE_MP44, false }, + { ACT_DOD_WALK_IDLE, ACT_DOD_WALK_IDLE_MP44, false }, + { ACT_DOD_RUN_IDLE, ACT_DOD_RUN_IDLE_MP44, false }, + { ACT_SPRINT, ACT_DOD_SPRINT_IDLE_MP44, false }, + + // Attack ( prone? ) + { ACT_RANGE_ATTACK1, ACT_DOD_PRIMARYATTACK_MP44, false }, + { ACT_DOD_PRIMARYATTACK_CROUCH, ACT_DOD_PRIMARYATTACK_MP44, false }, + { ACT_DOD_PRIMARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_MP44, false }, + + // Reload ( prone? ) + { ACT_RELOAD, ACT_DOD_RELOAD_MP44, false }, + { ACT_DOD_RELOAD_CROUCH, ACT_DOD_RELOAD_CROUCH_MP44, false }, + { ACT_DOD_RELOAD_PRONE, ACT_DOD_RELOAD_PRONE_MP44, false }, + + // Hand Signals + { ACT_DOD_HS_IDLE, ACT_DOD_HS_IDLE_MP44, false }, + { ACT_DOD_HS_CROUCH, ACT_DOD_HS_CROUCH_MP44, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponMP44 );
\ No newline at end of file diff --git a/game/shared/dod/weapon_p38.cpp b/game/shared/dod/weapon_p38.cpp new file mode 100644 index 0000000..348d720 --- /dev/null +++ b/game/shared/dod/weapon_p38.cpp @@ -0,0 +1,115 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodsemiauto.h" + +#if defined( CLIENT_DLL ) + +#define CWeaponP38 C_WeaponP38 + +#endif + + +class CWeaponP38 : public CDODSemiAutoWeapon +{ +public: + DECLARE_CLASS( CWeaponP38, CDODSemiAutoWeapon ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeaponP38() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_P38; } + + virtual Activity GetIdleActivity( void ); + virtual Activity GetPrimaryAttackActivity( void ); + virtual Activity GetDrawActivity( void ); + + virtual float GetRecoil( void ) { return 1.4f; } + +private: + CWeaponP38( const CWeaponP38 & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponP38, DT_WeaponP38 ) + +BEGIN_NETWORK_TABLE( CWeaponP38, DT_WeaponP38 ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponP38 ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_p38, CWeaponP38 ); +PRECACHE_WEAPON_REGISTER( weapon_p38 ); + +acttable_t CWeaponP38::m_acttable[] = +{ + { ACT_DOD_STAND_AIM, ACT_DOD_STAND_AIM_PISTOL, false }, + { ACT_DOD_CROUCH_AIM, ACT_DOD_CROUCH_AIM_PISTOL, false }, + { ACT_DOD_CROUCHWALK_AIM, ACT_DOD_CROUCHWALK_AIM_PISTOL, false }, + { ACT_DOD_WALK_AIM, ACT_DOD_WALK_AIM_PISTOL, false }, + { ACT_DOD_RUN_AIM, ACT_DOD_RUN_AIM_PISTOL, false }, + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_PISTOL, false }, + { ACT_PRONE_FORWARD, ACT_DOD_PRONEWALK_IDLE_PISTOL, false }, + { ACT_DOD_STAND_IDLE, ACT_DOD_STAND_IDLE_PISTOL, false }, + { ACT_DOD_CROUCH_IDLE, ACT_DOD_CROUCH_IDLE_PISTOL, false }, + { ACT_DOD_CROUCHWALK_IDLE, ACT_DOD_CROUCHWALK_IDLE_PISTOL, false }, + { ACT_DOD_WALK_IDLE, ACT_DOD_WALK_IDLE_PISTOL, false }, + { ACT_DOD_RUN_IDLE, ACT_DOD_RUN_IDLE_PISTOL, false }, + { ACT_SPRINT, ACT_DOD_SPRINT_IDLE_PISTOL, false }, + + { ACT_RANGE_ATTACK1, ACT_DOD_PRIMARYATTACK_PISTOL, false }, + { ACT_DOD_PRIMARYATTACK_CROUCH, ACT_DOD_PRIMARYATTACK_PISTOL, false }, + { ACT_DOD_PRIMARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_PISTOL, false }, + + { ACT_RELOAD, ACT_DOD_RELOAD_PISTOL, false }, + { ACT_DOD_RELOAD_CROUCH, ACT_DOD_RELOAD_CROUCH_PISTOL, false }, + { ACT_DOD_RELOAD_PRONE, ACT_DOD_RELOAD_PRONE_PISTOL, false }, + + // Hand Signals + { ACT_DOD_HS_IDLE, ACT_DOD_HS_IDLE_PISTOL, false }, + { ACT_DOD_HS_CROUCH, ACT_DOD_HS_CROUCH_PISTOL, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponP38 ); + +Activity CWeaponP38::GetIdleActivity( void ) +{ + Activity actIdle; + + if( m_iClip1 <= 0 ) + actIdle = ACT_VM_IDLE_EMPTY; + else + actIdle = ACT_VM_IDLE; + + return actIdle; +} + +Activity CWeaponP38::GetPrimaryAttackActivity( void ) +{ + Activity actPrim; + + if( m_iClip1 <= 0 ) + actPrim = ACT_VM_PRIMARYATTACK_EMPTY; + else + actPrim = ACT_VM_PRIMARYATTACK; + + return actPrim; +} + +Activity CWeaponP38::GetDrawActivity( void ) +{ + Activity actDraw; + + if( m_iClip1 <= 0 ) + actDraw = ACT_VM_DRAW_EMPTY; + else + actDraw = ACT_VM_DRAW; + + return actDraw; +}
\ No newline at end of file diff --git a/game/shared/dod/weapon_pschreck.cpp b/game/shared/dod/weapon_pschreck.cpp new file mode 100644 index 0000000..89e52fa --- /dev/null +++ b/game/shared/dod/weapon_pschreck.cpp @@ -0,0 +1,102 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodbaserpg.h" + +#ifndef CLIENT_DLL + #include "rocket_pschreck.h" +#endif + +#if defined( CLIENT_DLL ) + + #define CWeaponPschreck C_WeaponPschreck + +#endif + + +class CWeaponPschreck : public CDODBaseRocketWeapon +{ +public: + DECLARE_CLASS( CWeaponPschreck, CDODBaseRocketWeapon ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeaponPschreck() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_PSCHRECK; } + + virtual void FireRocket( void ); + +private: + CWeaponPschreck( const CWeaponPschreck & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponPschreck, DT_WeaponPschreck ) + +BEGIN_NETWORK_TABLE( CWeaponPschreck, DT_WeaponPschreck ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponPschreck ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_pschreck, CWeaponPschreck ); +PRECACHE_WEAPON_REGISTER( weapon_pschreck ); + +acttable_t CWeaponPschreck::m_acttable[] = +{ + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_PSCHRECK, false }, + { ACT_PRONE_FORWARD, ACT_DOD_PRONEWALK_IDLE_PSCHRECK, false }, + { ACT_DOD_STAND_IDLE, ACT_DOD_STAND_IDLE_PSCHRECK, false }, + { ACT_DOD_CROUCH_IDLE, ACT_DOD_CROUCH_IDLE_PSCHRECK, false }, + { ACT_DOD_CROUCHWALK_IDLE, ACT_DOD_CROUCHWALK_IDLE_PSCHRECK, false }, + { ACT_DOD_WALK_IDLE, ACT_DOD_WALK_IDLE_PSCHRECK, false }, + { ACT_DOD_RUN_IDLE, ACT_DOD_RUN_IDLE_PSCHRECK, false }, + { ACT_SPRINT, ACT_DOD_SPRINT_IDLE_PSCHRECK, false }, + + // Zoomed Aim + { ACT_DOD_IDLE_ZOOMED, ACT_DOD_STAND_ZOOM_PSCHRECK, false }, + { ACT_DOD_CROUCH_ZOOMED, ACT_DOD_CROUCH_ZOOM_PSCHRECK, false }, + { ACT_DOD_CROUCHWALK_ZOOMED, ACT_DOD_CROUCHWALK_ZOOM_PSCHRECK, false }, + { ACT_DOD_WALK_ZOOMED, ACT_DOD_WALK_ZOOM_PSCHRECK, false }, + { ACT_DOD_PRONE_ZOOMED, ACT_DOD_PRONE_ZOOM_PSCHRECK, false }, + { ACT_DOD_PRONE_FORWARD_ZOOMED, ACT_DOD_PRONE_ZOOM_FORWARD_PSCHRECK, false }, + + // Attack ( must be zoomed ) + { ACT_RANGE_ATTACK1, ACT_DOD_PRIMARYATTACK_PSCHRECK, false }, + { ACT_DOD_PRIMARYATTACK_CROUCH, ACT_DOD_PRIMARYATTACK_PSCHRECK, false }, + { ACT_DOD_PRIMARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_PSCHRECK, false }, + + // Reload ( zoomed or not, prone or not ) + { ACT_RELOAD, ACT_DOD_RELOAD_PSCHRECK, false }, + { ACT_DOD_RELOAD_CROUCH, ACT_DOD_RELOAD_CROUCH_PSCHRECK, false }, + { ACT_DOD_RELOAD_PRONE, ACT_DOD_RELOAD_PRONE_PSCHRECK, false }, + { ACT_DOD_RELOAD_DEPLOYED, ACT_DOD_ZOOMLOAD_PSCHRECK, false }, + { ACT_DOD_RELOAD_PRONE_DEPLOYED, ACT_DOD_ZOOMLOAD_PRONE_PSCHRECK, false }, + + // Hand Signals + { ACT_DOD_HS_IDLE, ACT_DOD_HS_IDLE_PSCHRECK, false }, + { ACT_DOD_HS_CROUCH, ACT_DOD_HS_CROUCH_PSCHRECK, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponPschreck ); + +void CWeaponPschreck::FireRocket( void ) +{ +#ifndef CLIENT_DLL + + CBasePlayer *pPlayer = GetPlayerOwner(); + +#ifdef DBGFLAG_ASSERT + CPschreckRocket *pRocket = +#endif //DEBUG + CPschreckRocket::Create( pPlayer->Weapon_ShootPosition(), pPlayer->GetAbsAngles(), pPlayer ); + + Assert( pRocket ); + +#endif +} diff --git a/game/shared/dod/weapon_riflegrenade.cpp b/game/shared/dod/weapon_riflegrenade.cpp new file mode 100644 index 0000000..b6c7abc --- /dev/null +++ b/game/shared/dod/weapon_riflegrenade.cpp @@ -0,0 +1,233 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "fx_dod_shared.h" +#include "weapon_riflegrenade.h" + +#ifdef CLIENT_DLL + #include "prediction.h" +#endif + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponBaseRifleGrenade, DT_WeaponBaseRifleGrenade ) + +BEGIN_NETWORK_TABLE( CWeaponBaseRifleGrenade, DT_WeaponBaseRifleGrenade ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponBaseRifleGrenade ) +END_PREDICTION_DATA() + + +CWeaponBaseRifleGrenade::CWeaponBaseRifleGrenade() +{ +} + +void CWeaponBaseRifleGrenade::Spawn() +{ + BaseClass::Spawn(); +} + +void CWeaponBaseRifleGrenade::PrimaryAttack( void ) +{ + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + + Assert( pPlayer ); + + // Out of ammo? + if ( m_iClip1 <= 0 ) + { + if (m_bFireOnEmpty) + { + PlayEmptySound(); + m_flNextPrimaryAttack = gpGlobals->curtime + 0.2; + } + return; + } + + if( pPlayer->GetWaterLevel() > 2 ) + { + PlayEmptySound(); + m_flNextPrimaryAttack = gpGlobals->curtime + 1.0; + return; + } + + // decrement before calling PlayPrimaryAttackAnim, so we can play the empty anim if necessary + m_iClip1--; + + SendWeaponAnim( ACT_VM_PRIMARYATTACK ); + + // player "shoot" animation + pPlayer->SetAnimation( PLAYER_ATTACK1 ); + +#ifdef CLIENT_DLL + if( pPlayer && !pPlayer->IsDormant() ) + pPlayer->DoAnimationEvent( PLAYERANIMEVENT_FIRE_GUN ); +#else + if( pPlayer ) + pPlayer->DoAnimationEvent( PLAYERANIMEVENT_FIRE_GUN ); +#endif + +#ifndef CLIENT_DLL + ShootGrenade(); +#endif + + WeaponSound( SINGLE ); + + DoFireEffects(); + +#ifdef CLIENT_DLL + CDODPlayer *p = ToDODPlayer( GetPlayerOwner() ); + + if ( prediction->IsFirstTimePredicted() ) + p->DoRecoil( GetWeaponID(), GetRecoil() ); +#endif + + m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime + GetFireDelay(); + m_flTimeWeaponIdle = gpGlobals->curtime + m_pWeaponInfo->m_flTimeToIdleAfterFire; + +#ifndef CLIENT_DLL + IGameEvent * event = gameeventmanager->CreateEvent( "dod_stats_weapon_attack" ); + if ( event ) + { + event->SetInt( "attacker", pPlayer->GetUserID() ); + event->SetInt( "weapon", GetStatsWeaponID() ); + + gameeventmanager->FireEvent( event ); + } +#endif //CLIENT_DLL +} + +Activity CWeaponBaseRifleGrenade::GetDrawActivity( void ) +{ + return ( (m_iClip1 <= 0 ) ? ACT_VM_DRAW_EMPTY : ACT_VM_DRAW ); +} + +Activity CWeaponBaseRifleGrenade::GetIdleActivity( void ) +{ + return ( (m_iClip1 <= 0 ) ? ACT_VM_IDLE_EMPTY : ACT_VM_IDLE ); +} + +bool CWeaponBaseRifleGrenade::Holster( CBaseCombatWeapon *pSwitchingTo ) +{ +#ifndef CLIENT_DLL + // If they attempt to switch weapons before the throw animation is done, + // allow it, but kill the weapon if we have to. + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + + if( pPlayer && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0 && m_iClip1 <= 0 ) + { + pPlayer->Weapon_Drop( this, NULL, NULL ); + UTIL_Remove(this); + return true; + } +#endif + + return BaseClass::Holster( pSwitchingTo ); +} + +// weapon idle to destroy weapon if empty +void CWeaponBaseRifleGrenade::WeaponIdle( void ) +{ +#ifndef CLIENT_DLL + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + if (pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0 && m_iClip1 <= 0) + { + pPlayer->Weapon_Drop( this, NULL, NULL ); + UTIL_Remove(this); + return; + } +#endif + + BaseClass::WeaponIdle(); +} + +#define DOD_RIFLEGRENADE_SPEED 2000 +extern ConVar dod_grenadegravity; + +#ifndef CLIENT_DLL + + void CWeaponBaseRifleGrenade::ShootGrenade( void ) + { + CDODPlayer *pPlayer = ToDODPlayer( GetOwner() ); + if ( !pPlayer ) + { + Assert( false ); + return; + } + + QAngle angThrow = pPlayer->LocalEyeAngles(); + + Vector vForward, vRight, vUp; + AngleVectors( angThrow, &vForward, &vRight, &vUp ); + + Vector eyes = pPlayer->GetAbsOrigin() + pPlayer->GetViewOffset(); + Vector vecSrc = eyes; + Vector vecThrow = vForward * DOD_RIFLEGRENADE_SPEED; + + // raise origin enough so that you can't shoot it straight down through the world + if ( pPlayer->m_Shared.IsProne() ) + { + vecSrc.z += 16; + } + + SetCollisionGroup( COLLISION_GROUP_WEAPON ); + + QAngle angles; + VectorAngles( -vecThrow, angles ); + + // estimate angular velocity based on initial conditions + // uses a constant ang velocity instead of the derivative of the flight path, oh well. + AngularImpulse angImpulse; + angImpulse.Init(); + + if ( vecThrow.z > 0 ) + angImpulse.y = -angles.x / ( sqrt( (-2 * vecThrow.z) / dod_grenadegravity.GetFloat() )); + else + angImpulse.y = -10; + + EmitGrenade( vecSrc, angles, vecThrow, angImpulse, pPlayer, RIFLEGRENADE_FUSE_LENGTH ); + } + + void CWeaponBaseRifleGrenade::EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer, float flLifeTime ) + { + Assert( !"Deriving classes should define this" ); + } + +#endif + +bool CWeaponBaseRifleGrenade::CanDeploy( void ) +{ + // does the player own the weapon that fires this grenade? + CBasePlayer *pPlayer = GetPlayerOwner(); + + if ( !pPlayer ) + return false; + + CBaseCombatWeapon *pOwnedWeapon = pPlayer->Weapon_OwnsThisType( GetCompanionWeaponName() ); + + if ( pOwnedWeapon == NULL ) + return false; + + return BaseClass::CanDeploy(); +} + +const char *CWeaponBaseRifleGrenade::GetCompanionWeaponName( void ) +{ + Assert( !"Should not call this version. Inheriting classes must implement." ); + return ""; +} + +float CWeaponBaseRifleGrenade::GetRecoil( void ) +{ + CDODPlayer *p = ToDODPlayer( GetPlayerOwner() ); + + if( p && p->m_Shared.IsInMGDeploy() ) + { + return 0.0f; + } + + return 10; +}
\ No newline at end of file diff --git a/game/shared/dod/weapon_riflegrenade.h b/game/shared/dod/weapon_riflegrenade.h new file mode 100644 index 0000000..f989f53 --- /dev/null +++ b/game/shared/dod/weapon_riflegrenade.h @@ -0,0 +1,48 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "shake.h" +#include "weapon_dodbasegun.h" + +#if defined( CLIENT_DLL ) +#define CWeaponBaseRifleGrenade C_WeaponBaseRifleGrenade +#endif + +class CWeaponBaseRifleGrenade : public CWeaponDODBaseGun +{ +public: + DECLARE_CLASS( CWeaponBaseRifleGrenade, CWeaponDODBaseGun ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + + CWeaponBaseRifleGrenade(); + + virtual void Spawn(); + virtual void PrimaryAttack( void ); + virtual bool Holster( CBaseCombatWeapon *pSwitchingTo ); + virtual void WeaponIdle( void ); + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_NONE; } + + + virtual Activity GetDrawActivity( void ); + virtual Activity GetIdleActivity( void ); + + virtual bool CanDeploy( void ); + + virtual const char *GetCompanionWeaponName( void ); + + virtual float GetRecoil( void ); + +#ifndef CLIENT_DLL + void ShootGrenade( void ); + virtual void EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer, float flLifeTime ); +#endif + +private: + CWeaponBaseRifleGrenade( const CWeaponBaseRifleGrenade & ); +};
\ No newline at end of file diff --git a/game/shared/dod/weapon_riflegrenade_ger.cpp b/game/shared/dod/weapon_riflegrenade_ger.cpp new file mode 100644 index 0000000..99afd1b --- /dev/null +++ b/game/shared/dod/weapon_riflegrenade_ger.cpp @@ -0,0 +1,91 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_riflegrenade.h" + +#if defined( CLIENT_DLL ) + +#include "c_dod_player.h" +#define CWeaponRifleGrenadeGER C_WeaponRifleGrenadeGER + +#else + +#include "dod_riflegrenade_ger.h" +#include "dod_player.h" + +#endif + +class CWeaponRifleGrenadeGER : public CWeaponBaseRifleGrenade +{ +public: + DECLARE_CLASS( CWeaponRifleGrenadeGER, CWeaponBaseRifleGrenade ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeaponRifleGrenadeGER() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_RIFLEGREN_GER; } + +#ifndef CLIENT_DLL + virtual void EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer, float flLifeTime = GRENADE_FUSE_LENGTH ) + { + CDODRifleGrenadeGER::Create( vecSrc, vecAngles, vecVel, angImpulse, pPlayer, flLifeTime, GetWeaponID() ); + } +#endif + + virtual const char *GetCompanionWeaponName( void ) + { + return "weapon_k98"; + } + +private: + CWeaponRifleGrenadeGER( const CWeaponRifleGrenadeGER & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponRifleGrenadeGER, DT_WeaponRifleGrenadeGER ) + +BEGIN_NETWORK_TABLE( CWeaponRifleGrenadeGER, DT_WeaponRifleGrenadeGER ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponRifleGrenadeGER ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_riflegren_ger, CWeaponRifleGrenadeGER ); +PRECACHE_WEAPON_REGISTER( weapon_riflegren_ger ); + +acttable_t CWeaponRifleGrenadeGER::m_acttable[] = +{ + { ACT_DOD_STAND_AIM, ACT_DOD_STAND_AIM_RIFLE, false }, + { ACT_DOD_CROUCH_AIM, ACT_DOD_CROUCH_AIM_RIFLE, false }, + { ACT_DOD_CROUCHWALK_AIM, ACT_DOD_CROUCHWALK_AIM_RIFLE, false }, + { ACT_DOD_WALK_AIM, ACT_DOD_WALK_AIM_RIFLE, false }, + { ACT_DOD_RUN_AIM, ACT_DOD_RUN_AIM_RIFLE, false }, + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_RIFLE, false }, + { ACT_PRONE_FORWARD, ACT_DOD_PRONEWALK_IDLE_RIFLE, false }, + { ACT_DOD_STAND_IDLE, ACT_DOD_STAND_IDLE_RIFLE, false }, + { ACT_DOD_CROUCH_IDLE, ACT_DOD_CROUCH_IDLE_RIFLE, false }, + { ACT_DOD_CROUCHWALK_IDLE, ACT_DOD_CROUCHWALK_IDLE_RIFLE, false }, + { ACT_DOD_WALK_IDLE, ACT_DOD_WALK_IDLE_RIFLE, false }, + { ACT_DOD_RUN_IDLE, ACT_DOD_RUN_IDLE_RIFLE, false }, + { ACT_SPRINT, ACT_DOD_SPRINT_IDLE_RIFLE, false }, + + // Attack ( prone? deployed? ) + { ACT_RANGE_ATTACK1, ACT_DOD_PRIMARYATTACK_RIFLE, false }, + { ACT_DOD_PRIMARYATTACK_CROUCH, ACT_DOD_PRIMARYATTACK_RIFLE, false }, + { ACT_DOD_PRIMARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_RIFLE, false }, + + { ACT_RELOAD, ACT_DOD_RELOAD_RIFLEGRENADE, false }, + { ACT_DOD_RELOAD_CROUCH, ACT_DOD_RELOAD_CROUCH_RIFLEGRENADE, false }, + { ACT_DOD_RELOAD_PRONE, ACT_DOD_RELOAD_PRONE_RIFLEGRENADE, false }, + + // Hand Signals + { ACT_DOD_HS_IDLE, ACT_DOD_HS_IDLE_K98, false }, + { ACT_DOD_HS_CROUCH, ACT_DOD_HS_CROUCH_K98, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponRifleGrenadeGER ); diff --git a/game/shared/dod/weapon_riflegrenade_ger_live.cpp b/game/shared/dod/weapon_riflegrenade_ger_live.cpp new file mode 100644 index 0000000..fce54d1 --- /dev/null +++ b/game/shared/dod/weapon_riflegrenade_ger_live.cpp @@ -0,0 +1,56 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodbasegrenade.h" + +#ifdef CLIENT_DLL +#define CWeaponRifleGrenadeGER_Live C_WeaponRifleGrenadeGER_Live +#else +#include "dod_riflegrenade_ger.h" //the thing that we throw +#endif + + +class CWeaponRifleGrenadeGER_Live : public CWeaponDODBaseGrenade +{ +public: + DECLARE_CLASS( CWeaponRifleGrenadeGER_Live, CWeaponDODBaseGrenade ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + + CWeaponRifleGrenadeGER_Live() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_RIFLEGREN_GER_LIVE; } + +#ifndef CLIENT_DLL + + virtual void EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer, float flLifeTime = GRENADE_FUSE_LENGTH ) + { + // align the grenade vertically and spin end over end + QAngle vecNewAngles = QAngle(45,pPlayer->EyeAngles().y,0); + AngularImpulse angNewImpulse = AngularImpulse( 0, 600, 0 ); + + CDODRifleGrenadeGER::Create( vecSrc, vecNewAngles, vecVel, angNewImpulse, pPlayer, flLifeTime, GetWeaponID() ); + } + +#endif + + virtual bool IsArmed( void ) { return true; } + +private: + CWeaponRifleGrenadeGER_Live( const CWeaponRifleGrenadeGER_Live & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponRifleGrenadeGER_Live, DT_WeaponRifleGrenadeGER_Live ) + +BEGIN_NETWORK_TABLE(CWeaponRifleGrenadeGER_Live, DT_WeaponRifleGrenadeGER_Live) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponRifleGrenadeGER_Live ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_riflegren_ger_live, CWeaponRifleGrenadeGER_Live ); +PRECACHE_WEAPON_REGISTER( weapon_riflegren_ger_live );
\ No newline at end of file diff --git a/game/shared/dod/weapon_riflegrenade_us.cpp b/game/shared/dod/weapon_riflegrenade_us.cpp new file mode 100644 index 0000000..db152d0 --- /dev/null +++ b/game/shared/dod/weapon_riflegrenade_us.cpp @@ -0,0 +1,91 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_riflegrenade.h" + +#if defined( CLIENT_DLL ) + + #include "c_dod_player.h" + #define CWeaponRifleGrenadeUS C_WeaponRifleGrenadeUS + +#else + + #include "dod_riflegrenade_us.h" + #include "dod_player.h" + +#endif + +class CWeaponRifleGrenadeUS : public CWeaponBaseRifleGrenade +{ +public: + DECLARE_CLASS( CWeaponRifleGrenadeUS, CWeaponBaseRifleGrenade ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeaponRifleGrenadeUS() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_RIFLEGREN_US; } + +#ifndef CLIENT_DLL + virtual void EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer, float flLifeTime = GRENADE_FUSE_LENGTH ) + { + CDODRifleGrenadeUS::Create( vecSrc, vecAngles, vecVel, angImpulse, pPlayer, flLifeTime, GetWeaponID() ); + } +#endif + + virtual const char *GetCompanionWeaponName( void ) + { + return "weapon_garand"; + } + +private: + CWeaponRifleGrenadeUS( const CWeaponRifleGrenadeUS & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponRifleGrenadeUS, DT_WeaponRifleGrenadeUS ) + +BEGIN_NETWORK_TABLE( CWeaponRifleGrenadeUS, DT_WeaponRifleGrenadeUS ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponRifleGrenadeUS ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_riflegren_us, CWeaponRifleGrenadeUS ); +PRECACHE_WEAPON_REGISTER( weapon_riflegren_us ); + +acttable_t CWeaponRifleGrenadeUS::m_acttable[] = +{ + { ACT_DOD_STAND_AIM, ACT_DOD_STAND_AIM_RIFLE, false }, + { ACT_DOD_CROUCH_AIM, ACT_DOD_CROUCH_AIM_RIFLE, false }, + { ACT_DOD_CROUCHWALK_AIM, ACT_DOD_CROUCHWALK_AIM_RIFLE, false }, + { ACT_DOD_WALK_AIM, ACT_DOD_WALK_AIM_RIFLE, false }, + { ACT_DOD_RUN_AIM, ACT_DOD_RUN_AIM_RIFLE, false }, + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_RIFLE, false }, + { ACT_PRONE_FORWARD, ACT_DOD_PRONEWALK_IDLE_RIFLE, false }, + { ACT_DOD_STAND_IDLE, ACT_DOD_STAND_IDLE_RIFLE, false }, + { ACT_DOD_CROUCH_IDLE, ACT_DOD_CROUCH_IDLE_RIFLE, false }, + { ACT_DOD_CROUCHWALK_IDLE, ACT_DOD_CROUCHWALK_IDLE_RIFLE, false }, + { ACT_DOD_WALK_IDLE, ACT_DOD_WALK_IDLE_RIFLE, false }, + { ACT_DOD_RUN_IDLE, ACT_DOD_RUN_IDLE_RIFLE, false }, + { ACT_SPRINT, ACT_DOD_SPRINT_IDLE_RIFLE, false }, + + // Attack ( prone? deployed? ) + { ACT_RANGE_ATTACK1, ACT_DOD_PRIMARYATTACK_RIFLE, false }, + { ACT_DOD_PRIMARYATTACK_CROUCH, ACT_DOD_PRIMARYATTACK_RIFLE, false }, + { ACT_DOD_PRIMARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_RIFLE, false }, + + { ACT_RELOAD, ACT_DOD_RELOAD_RIFLEGRENADE, false }, + { ACT_DOD_RELOAD_CROUCH, ACT_DOD_RELOAD_CROUCH_RIFLEGRENADE, false }, + { ACT_DOD_RELOAD_PRONE, ACT_DOD_RELOAD_PRONE_RIFLEGRENADE, false }, + + // Hand Signals + { ACT_DOD_HS_IDLE, ACT_DOD_HS_IDLE_K98, false }, + { ACT_DOD_HS_CROUCH, ACT_DOD_HS_CROUCH_K98, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponRifleGrenadeUS ); diff --git a/game/shared/dod/weapon_riflegrenade_us_live.cpp b/game/shared/dod/weapon_riflegrenade_us_live.cpp new file mode 100644 index 0000000..039c6fa --- /dev/null +++ b/game/shared/dod/weapon_riflegrenade_us_live.cpp @@ -0,0 +1,56 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodbasegrenade.h" + +#ifdef CLIENT_DLL +#define CWeaponRifleGrenadeUS_Live C_WeaponRifleGrenadeUS_Live +#else +#include "dod_riflegrenade_us.h" //the thing that we throw +#endif + + +class CWeaponRifleGrenadeUS_Live : public CWeaponDODBaseGrenade +{ +public: + DECLARE_CLASS( CWeaponRifleGrenadeUS_Live, CWeaponDODBaseGrenade ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + + CWeaponRifleGrenadeUS_Live() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_RIFLEGREN_US_LIVE; } + +#ifndef CLIENT_DLL + + virtual void EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer, float flLifeTime = GRENADE_FUSE_LENGTH ) + { + // align the grenade vertically and spin end over end + QAngle vecNewAngles = QAngle(45,pPlayer->EyeAngles().y,0); + AngularImpulse angNewImpulse = AngularImpulse( 0, 600, 0 ); + + CDODRifleGrenadeUS::Create( vecSrc, vecNewAngles, vecVel, angNewImpulse, pPlayer, flLifeTime, GetWeaponID() ); + } + +#endif + + virtual bool IsArmed( void ) { return true; } + +private: + CWeaponRifleGrenadeUS_Live( const CWeaponRifleGrenadeUS_Live & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponRifleGrenadeUS_Live, DT_WeaponRifleGrenadeUS_Live ) + +BEGIN_NETWORK_TABLE(CWeaponRifleGrenadeUS_Live, DT_WeaponRifleGrenadeUS_Live) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponRifleGrenadeUS_Live ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_riflegren_us_live, CWeaponRifleGrenadeUS_Live ); +PRECACHE_WEAPON_REGISTER( weapon_riflegren_us_live );
\ No newline at end of file diff --git a/game/shared/dod/weapon_smokegrenade_ger.cpp b/game/shared/dod/weapon_smokegrenade_ger.cpp new file mode 100644 index 0000000..6d9e310 --- /dev/null +++ b/game/shared/dod/weapon_smokegrenade_ger.cpp @@ -0,0 +1,55 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodbasegrenade.h" + +#ifdef CLIENT_DLL +#define CWeaponSmokeGrenadeGER C_WeaponSmokeGrenadeGER +#else +#include "dod_smokegrenade_ger.h" //the thing that we throw +#endif + +class CWeaponSmokeGrenadeGER : public CWeaponDODBaseGrenade +{ +public: + DECLARE_CLASS( CWeaponSmokeGrenadeGER, CWeaponDODBaseGrenade ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + + CWeaponSmokeGrenadeGER() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_SMOKE_GER; } + +#ifndef CLIENT_DLL + + virtual void EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer, float flLifeTime = GRENADE_FUSE_LENGTH ) + { + // align the stickgrenade vertically and spin end over end + QAngle vecNewAngles = QAngle(45,pPlayer->EyeAngles().y,0); + AngularImpulse angNewImpulse = AngularImpulse( 0, 600, 0 ); + + CDODSmokeGrenadeGER::Create( vecSrc, vecNewAngles, vecVel, angNewImpulse, pPlayer ); + } + +#endif + + virtual float GetDetonateTimerLength( void ) { return 10; } + +private: + CWeaponSmokeGrenadeGER( const CWeaponSmokeGrenadeGER & ) {} +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponSmokeGrenadeGER, DT_WeaponSmokeGrenadeGER ) + +BEGIN_NETWORK_TABLE(CWeaponSmokeGrenadeGER, DT_WeaponSmokeGrenadeGER) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponSmokeGrenadeGER ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_smoke_ger, CWeaponSmokeGrenadeGER ); +PRECACHE_WEAPON_REGISTER( weapon_smoke_ger );
\ No newline at end of file diff --git a/game/shared/dod/weapon_smokegrenade_us.cpp b/game/shared/dod/weapon_smokegrenade_us.cpp new file mode 100644 index 0000000..69c71ce --- /dev/null +++ b/game/shared/dod/weapon_smokegrenade_us.cpp @@ -0,0 +1,51 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodbasegrenade.h" + +#ifdef CLIENT_DLL + #define CWeaponSmokeGrenadeUS C_WeaponSmokeGrenadeUS +#else + #include "dod_smokegrenade_us.h" //the thing that we throw +#endif + +class CWeaponSmokeGrenadeUS : public CWeaponDODBaseGrenade +{ +public: + DECLARE_CLASS( CWeaponSmokeGrenadeUS, CWeaponDODBaseGrenade ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + + CWeaponSmokeGrenadeUS() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_SMOKE_US; } + +#ifndef CLIENT_DLL + + virtual void EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer, float flLifeTime = GRENADE_FUSE_LENGTH ) + { + CDODSmokeGrenadeUS::Create( vecSrc, vecAngles, vecVel, angImpulse, pPlayer ); + } + +#endif + + virtual float GetDetonateTimerLength( void ) { return 10; } + +private: + CWeaponSmokeGrenadeUS( const CWeaponSmokeGrenadeUS & ) {} +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponSmokeGrenadeUS, DT_WeaponSmokeGrenadeUS ) + +BEGIN_NETWORK_TABLE(CWeaponSmokeGrenadeUS, DT_WeaponSmokeGrenadeUS) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponSmokeGrenadeUS ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_smoke_us, CWeaponSmokeGrenadeUS ); +PRECACHE_WEAPON_REGISTER( weapon_smoke_us );
\ No newline at end of file diff --git a/game/shared/dod/weapon_spade.cpp b/game/shared/dod/weapon_spade.cpp new file mode 100644 index 0000000..4df7d21 --- /dev/null +++ b/game/shared/dod/weapon_spade.cpp @@ -0,0 +1,71 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodbasemelee.h" + +#if defined( CLIENT_DLL ) + + #define CWeaponSpade C_WeaponSpade + +#endif + + +class CWeaponSpade : public CWeaponDODBaseMelee +{ +public: + DECLARE_CLASS( CWeaponSpade, CWeaponDODBaseMelee ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeaponSpade() {} + + virtual Activity GetMeleeActivity( void ) { return ACT_VM_PRIMARYATTACK; } + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_SPADE; } + +private: + CWeaponSpade( const CWeaponSpade & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponSpade, DT_WeaponSpade ) + +BEGIN_NETWORK_TABLE( CWeaponSpade, DT_WeaponSpade ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponSpade ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_spade, CWeaponSpade ); +PRECACHE_WEAPON_REGISTER( weapon_spade ); + +acttable_t CWeaponSpade::m_acttable[] = +{ + { ACT_DOD_STAND_AIM, ACT_DOD_STAND_AIM_SPADE, false }, + { ACT_DOD_CROUCH_AIM, ACT_DOD_CROUCH_AIM_SPADE, false }, + { ACT_DOD_CROUCHWALK_AIM, ACT_DOD_CROUCHWALK_AIM_SPADE, false }, + { ACT_DOD_WALK_AIM, ACT_DOD_WALK_AIM_SPADE, false }, + { ACT_DOD_RUN_AIM, ACT_DOD_RUN_AIM_SPADE, false }, + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_SPADE, false }, + { ACT_PRONE_FORWARD, ACT_DOD_PRONEWALK_AIM_SPADE, false }, + { ACT_DOD_STAND_IDLE, ACT_DOD_STAND_AIM_SPADE, false }, + { ACT_DOD_CROUCH_IDLE, ACT_DOD_CROUCH_AIM_SPADE, false }, + { ACT_DOD_CROUCHWALK_IDLE, ACT_DOD_CROUCHWALK_AIM_SPADE, false }, + { ACT_DOD_WALK_IDLE, ACT_DOD_WALK_AIM_SPADE, false }, + { ACT_DOD_RUN_IDLE, ACT_DOD_RUN_AIM_SPADE, false }, + { ACT_SPRINT, ACT_DOD_SPRINT_AIM_SPADE, false }, + + { ACT_RANGE_ATTACK2, ACT_DOD_PRIMARYATTACK_SPADE, false }, + { ACT_DOD_SECONDARYATTACK_CROUCH, ACT_DOD_PRIMARYATTACK_CROUCH_SPADE, false }, + { ACT_DOD_SECONDARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_SPADE, false }, + + // Hand Signals + { ACT_DOD_HS_IDLE, ACT_DOD_HS_IDLE_STICKGRENADE, false }, + { ACT_DOD_HS_CROUCH, ACT_DOD_HS_CROUCH_STICKGRENADE, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponSpade );
\ No newline at end of file diff --git a/game/shared/dod/weapon_spring.cpp b/game/shared/dod/weapon_spring.cpp new file mode 100644 index 0000000..9f9ed67 --- /dev/null +++ b/game/shared/dod/weapon_spring.cpp @@ -0,0 +1,156 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodsniper.h" + +#if defined( CLIENT_DLL ) + + #define CWeaponSpring C_WeaponSpring + +#endif + + +class CWeaponSpring : public CDODSniperWeapon +{ +public: + DECLARE_CLASS( CWeaponSpring, CDODSniperWeapon ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeaponSpring() {} + + virtual void Spawn( void ); + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_SPRING; } + + // weapon id for stats purposes + virtual DODWeaponID GetStatsWeaponID( void ) + { + if ( IsFullyZoomed() ) + return WEAPON_SPRING_ZOOMED; + else + return WEAPON_SPRING; + } + + virtual bool ShouldAutoEjectBrass( void ) { return false; } + virtual bool ShouldDrawCrosshair( void ) { return false; } + + virtual bool ShouldDrawViewModel( void ) + { + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); + + Assert( pPlayer ); + + if ( !pPlayer ) + return false; + + if ( pPlayer->GetFOV() < 90 ) + return false; + + // handle case when we are spectating someone and don't know their fov + if ( IsFullyZoomed() ) + return false; + + return BaseClass::ShouldDrawViewModel(); + } + + virtual bool ShouldDrawMuzzleFlash( void ) + { + return ShouldDrawViewModel(); + } + + virtual Activity GetPrimaryAttackActivity( void ); + + virtual float GetFireDelay( void ); + + virtual float GetRecoil( void ) { return 5.6f; } + +private: + CWeaponSpring( const CWeaponSpring & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponSpring, DT_WeaponSpring ) + +BEGIN_NETWORK_TABLE( CWeaponSpring, DT_WeaponSpring ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponSpring ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_spring, CWeaponSpring ); +PRECACHE_WEAPON_REGISTER( weapon_spring ); + +void CWeaponSpring::Spawn( void ) +{ + m_iAltFireHint = HINT_USE_ZOOM; + + BaseClass::Spawn(); +} + +acttable_t CWeaponSpring::m_acttable[] = +{ + { ACT_DOD_STAND_AIM, ACT_DOD_STAND_AIM_BOLT, false }, + { ACT_DOD_CROUCH_AIM, ACT_DOD_CROUCH_AIM_BOLT, false }, + { ACT_DOD_CROUCHWALK_AIM, ACT_DOD_CROUCHWALK_AIM_BOLT, false }, + { ACT_DOD_WALK_AIM, ACT_DOD_WALK_AIM_BOLT, false }, + { ACT_DOD_RUN_AIM, ACT_DOD_RUN_AIM_BOLT, false }, + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_BOLT, false }, + { ACT_PRONE_FORWARD, ACT_DOD_PRONEWALK_IDLE_BOLT, false }, + { ACT_DOD_STAND_IDLE, ACT_DOD_STAND_IDLE_BOLT, false }, + { ACT_DOD_CROUCH_IDLE, ACT_DOD_CROUCH_IDLE_BOLT, false }, + { ACT_DOD_CROUCHWALK_IDLE, ACT_DOD_CROUCHWALK_IDLE_BOLT, false }, + { ACT_DOD_WALK_IDLE, ACT_DOD_WALK_IDLE_BOLT, false }, + { ACT_DOD_RUN_IDLE, ACT_DOD_RUN_IDLE_BOLT, false }, + { ACT_SPRINT, ACT_DOD_SPRINT_IDLE_BOLT, false }, + + // Zoomed Aim + { ACT_DOD_IDLE_ZOOMED, ACT_DOD_STAND_ZOOM_BOLT, false }, + { ACT_DOD_CROUCH_ZOOMED, ACT_DOD_CROUCH_ZOOM_BOLT, false }, + { ACT_DOD_CROUCHWALK_ZOOMED, ACT_DOD_CROUCHWALK_ZOOM_BOLT, false }, + { ACT_DOD_WALK_ZOOMED, ACT_DOD_WALK_ZOOM_BOLT, false }, + { ACT_DOD_PRONE_ZOOMED, ACT_DOD_PRONE_ZOOM_BOLT, false }, + { ACT_DOD_PRONE_FORWARD_ZOOMED, ACT_DOD_PRONE_ZOOM_FORWARD_BOLT, false }, + + // Attack ( prone? ) + { ACT_RANGE_ATTACK1, ACT_DOD_PRIMARYATTACK_BOLT, false }, + { ACT_DOD_PRIMARYATTACK_CROUCH, ACT_DOD_PRIMARYATTACK_BOLT, false }, + { ACT_DOD_PRIMARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_BOLT, false }, + + // Reload ( prone ? ) + { ACT_RELOAD, ACT_DOD_RELOAD_BOLT, false }, + { ACT_DOD_RELOAD_CROUCH, ACT_DOD_RELOAD_CROUCH_BOLT, false }, + { ACT_DOD_RELOAD_PRONE, ACT_DOD_RELOAD_PRONE_BOLT, false }, + + // Hand Signals + { ACT_DOD_HS_IDLE, ACT_DOD_HS_IDLE_K98, false }, + { ACT_DOD_HS_CROUCH, ACT_DOD_HS_CROUCH_K98, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponSpring ); + +Activity CWeaponSpring::GetPrimaryAttackActivity( void ) +{ + Activity actPrim; + + if( m_iClip1 <= 0 ) + actPrim = ACT_VM_PRIMARYATTACK_EMPTY; + else + actPrim = ACT_VM_PRIMARYATTACK; + + return actPrim; +} + +float CWeaponSpring::GetFireDelay( void ) +{ + if ( m_iClip1 <= 0 ) + { + return SequenceDuration(); + } + + return BaseClass::GetFireDelay(); +} diff --git a/game/shared/dod/weapon_stickgrenade.cpp b/game/shared/dod/weapon_stickgrenade.cpp new file mode 100644 index 0000000..b8e94c5 --- /dev/null +++ b/game/shared/dod/weapon_stickgrenade.cpp @@ -0,0 +1,82 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodbasegrenade.h" + +#ifdef CLIENT_DLL + #define CWeaponStickGrenade C_WeaponStickGrenade +#else + #include "dod_stickgrenade.h" +#endif + +class CWeaponStickGrenade : public CWeaponDODBaseGrenade +{ +public: + DECLARE_CLASS( CWeaponStickGrenade, CWeaponDODBaseGrenade ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeaponStickGrenade() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_FRAG_GER; } + +#ifndef CLIENT_DLL + + virtual void EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer, float flLifeTime = GRENADE_FUSE_LENGTH ) + { + // align the stickgrenade vertically and spin end over end + QAngle vecNewAngles = QAngle(45,pPlayer->EyeAngles().y,0); + AngularImpulse angNewImpulse = AngularImpulse( 0, 600, 0 ); + + CDODStickGrenade::Create( vecSrc, vecNewAngles, vecVel, angNewImpulse, pPlayer, flLifeTime, GetWeaponID() ); + } + +#endif + + CWeaponStickGrenade( const CWeaponStickGrenade & ) {} +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponStickGrenade, DT_WeaponStickGrenade ) + +BEGIN_NETWORK_TABLE(CWeaponStickGrenade, DT_WeaponStickGrenade) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponStickGrenade ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_frag_ger, CWeaponStickGrenade ); +PRECACHE_WEAPON_REGISTER( weapon_frag_ger ); + +acttable_t CWeaponStickGrenade::m_acttable[] = +{ + // Move this out to the specific grenades??? + { ACT_DOD_STAND_AIM, ACT_DOD_STAND_AIM_GREN_STICK, false }, + { ACT_DOD_CROUCH_AIM, ACT_DOD_CROUCH_AIM_GREN_STICK, false }, + { ACT_DOD_CROUCHWALK_AIM, ACT_DOD_CROUCHWALK_AIM_GREN_STICK, false }, + { ACT_DOD_WALK_AIM, ACT_DOD_WALK_AIM_GREN_STICK, false }, + { ACT_DOD_RUN_AIM, ACT_DOD_RUN_AIM_GREN_STICK, false }, + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_GREN_STICK, false }, + { ACT_PRONE_FORWARD, ACT_DOD_PRONEWALK_AIM_GREN_STICK, false }, + { ACT_DOD_STAND_IDLE, ACT_DOD_STAND_AIM_GREN_STICK, false }, + { ACT_DOD_CROUCH_IDLE, ACT_DOD_CROUCH_AIM_GREN_STICK, false }, + { ACT_DOD_CROUCHWALK_IDLE, ACT_DOD_CROUCHWALK_AIM_GREN_STICK, false }, + { ACT_DOD_WALK_IDLE, ACT_DOD_WALK_AIM_GREN_STICK, false }, + { ACT_DOD_RUN_IDLE, ACT_DOD_RUN_AIM_GREN_STICK, false }, + { ACT_SPRINT, ACT_DOD_SPRINT_AIM_GREN_STICK, false }, + + { ACT_RANGE_ATTACK1, ACT_DOD_PRIMARYATTACK_GREN_STICK, false }, + { ACT_DOD_PRIMARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_GREN_STICK, false }, + { ACT_DOD_PRIMARYATTACK_CROUCH, ACT_DOD_PRIMARYATTACK_CROUCH_GREN_STICK, false }, + + // Hand Signals + { ACT_DOD_HS_IDLE, ACT_DOD_HS_IDLE_STICKGRENADE, false }, + { ACT_DOD_HS_CROUCH, ACT_DOD_HS_CROUCH_STICKGRENADE, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponStickGrenade ); + diff --git a/game/shared/dod/weapon_thompson.cpp b/game/shared/dod/weapon_thompson.cpp new file mode 100644 index 0000000..d90e4f6 --- /dev/null +++ b/game/shared/dod/weapon_thompson.cpp @@ -0,0 +1,81 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_dodfullauto_punch.h" + + +#if defined( CLIENT_DLL ) + + #define CWeaponThompson C_WeaponThompson + +#endif + + +class CWeaponThompson : public CDODFullAutoPunchWeapon +{ +public: + DECLARE_CLASS( CWeaponThompson, CDODFullAutoPunchWeapon ); + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + DECLARE_ACTTABLE(); + + CWeaponThompson() {} + + virtual DODWeaponID GetWeaponID( void ) const { return WEAPON_THOMPSON; } + virtual DODWeaponID GetAltWeaponID( void ) const { return WEAPON_THOMPSON_PUNCH; } + + virtual float GetRecoil( void ) { return 2.15f; } + +private: + CWeaponThompson( const CWeaponThompson & ); +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponThompson, DT_WeaponThompson ) + +BEGIN_NETWORK_TABLE( CWeaponThompson, DT_WeaponThompson ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponThompson ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_thompson, CWeaponThompson ); +PRECACHE_WEAPON_REGISTER( weapon_thompson ); + +acttable_t CWeaponThompson::m_acttable[] = +{ + { ACT_DOD_STAND_AIM, ACT_DOD_STAND_AIM_TOMMY, false }, + { ACT_DOD_CROUCH_AIM, ACT_DOD_CROUCH_AIM_TOMMY, false }, + { ACT_DOD_CROUCHWALK_AIM, ACT_DOD_CROUCHWALK_AIM_TOMMY, false }, + { ACT_DOD_WALK_AIM, ACT_DOD_WALK_AIM_TOMMY, false }, + { ACT_DOD_RUN_AIM, ACT_DOD_RUN_AIM_TOMMY, false }, + { ACT_PRONE_IDLE, ACT_DOD_PRONE_AIM_TOMMY, false }, + { ACT_PRONE_FORWARD, ACT_DOD_PRONEWALK_IDLE_TOMMY, false }, + { ACT_DOD_STAND_IDLE, ACT_DOD_STAND_IDLE_TOMMY, false }, + { ACT_DOD_CROUCH_IDLE, ACT_DOD_CROUCH_IDLE_TOMMY, false }, + { ACT_DOD_CROUCHWALK_IDLE, ACT_DOD_CROUCHWALK_IDLE_TOMMY, false }, + { ACT_DOD_WALK_IDLE, ACT_DOD_WALK_IDLE_TOMMY, false }, + { ACT_DOD_RUN_IDLE, ACT_DOD_RUN_IDLE_TOMMY, false }, + { ACT_SPRINT, ACT_DOD_SPRINT_IDLE_TOMMY, false }, + + { ACT_RANGE_ATTACK1, ACT_DOD_PRIMARYATTACK_TOMMY, false }, + { ACT_DOD_PRIMARYATTACK_CROUCH, ACT_DOD_PRIMARYATTACK_TOMMY, false }, + { ACT_DOD_PRIMARYATTACK_PRONE, ACT_DOD_PRIMARYATTACK_PRONE_TOMMY, false }, + { ACT_RANGE_ATTACK2, ACT_DOD_SECONDARYATTACK_TOMMY, false }, + { ACT_DOD_SECONDARYATTACK_CROUCH, ACT_DOD_SECONDARYATTACK_CROUCH_TOMMY, false }, + { ACT_DOD_SECONDARYATTACK_PRONE, ACT_DOD_SECONDARYATTACK_PRONE_TOMMY, false }, + + { ACT_RELOAD, ACT_DOD_RELOAD_TOMMY, false }, + { ACT_DOD_RELOAD_CROUCH, ACT_DOD_RELOAD_CROUCH_TOMMY, false }, + { ACT_DOD_RELOAD_PRONE, ACT_DOD_RELOAD_PRONE_TOMMY, false }, + + // Hand Signals + { ACT_DOD_HS_IDLE, ACT_DOD_HS_IDLE_TOMMY, false }, + { ACT_DOD_HS_CROUCH, ACT_DOD_HS_CROUCH_TOMMY, false }, +}; + +IMPLEMENT_ACTTABLE( CWeaponThompson ); + |