diff --git a/bin/swarm.fgd b/bin/swarm.fgd index 3f108d8..d7a7cba 100644 --- a/bin/swarm.fgd +++ b/bin/swarm.fgd @@ -4793,8 +4793,8 @@ 0 : "No" 1 : "Yes" ] - MinSkillLevel(integer) : "Min Skill Level" : 0 : "The minimum skill level for this spawner to function. Skill levels are: 1=easy 2=normal 3=hard 4=insane" - MaxSkillLevel(integer) : "Max Skill Level" : 0 : "The maximum skill level for this spawner to function. Skill levels are: 1=easy 2=normal 3=hard 4=insane" + MinSkillLevel(integer) : "Min Skill Level" : 0 : "The minimum skill level for this spawner to function. Skill levels are: 1=easy 2=normal 3=hard 4=insane 5=imba" + MaxSkillLevel(integer) : "Max Skill Level" : 0 : "The maximum skill level for this spawner to function. Skill levels are: 1=easy 2=normal 3=hard 4=insane 5=imba" output OnSpawned(string) : "Fires when an alien is spawned. The activator is the alien, and the string is the name of the alien." diff --git a/sdk_src/game/client/swarm/c_asw_steamstats.cpp b/sdk_src/game/client/swarm/c_asw_steamstats.cpp index 4f00894..9692b75 100644 --- a/sdk_src/game/client/swarm/c_asw_steamstats.cpp +++ b/sdk_src/game/client/swarm/c_asw_steamstats.cpp @@ -11,6 +11,7 @@ #include #include "asw_shareddefs.h" #include "c_asw_marine_resource.h" +#include "c_asw_campaign_save.h" CASW_Steamstats g_ASW_Steamstats; @@ -39,15 +40,56 @@ namespace const char* szShotsTotal = ".shotsfired.total"; const char* szShotsHit = ".shotshit.total"; const char* szHealingTotal = ".healing.total"; + const char* szTimeTotal = ".time.total"; + const char* szKillsAvg = ".kills.avg"; + const char* szDamageAvg = ".damage.avg"; + const char* szFFAvg = ".ff.avg"; + const char* szTimeAvg = ".time.avg"; + const char* szBestDifficulty = ".difficulty.best"; + const char* szBestTime = ".time.best"; + const char* szBestSpeedrunDifficulty = ".time.best.difficulty"; + // difficulty names used when fetching steam stats const char* g_szDifficulties[] = { "Easy", "Normal", "Hard", - "Insane" + "Insane", + "imba" }; + const char *g_OfficialMaps[] = + { + "asi-jac1-landingbay_01", + "asi-jac1-landingbay_02", + "asi-jac2-deima", + "asi-jac3-rydberg", + "asi-jac4-residential", + "asi-jac6-sewerjunction", + "asi-jac7-timorstation" + }; +} + +bool IsOfficialCampaign() +{ + if( !ASWGameRules()->IsCampaignGame() ) + return false; + + CASW_Campaign_Save *pCampaign = ASWGameRules()->GetCampaignSave(); + + const char *szMapName = engine->GetLevelNameShort(); + const char *szCampaignName = pCampaign->GetCampaignName(); + if( FStrEq( szCampaignName, "jacob" ) ) + { + for( int i=0; i < ARRAYSIZE( g_OfficialMaps ); ++i ) + { + if( FStrEq( szMapName, g_OfficialMaps[i] ) ) + return true; + } + } + + return false; } bool IsDamagingWeapon( const char* szWeaponName, bool bIsExtraEquip ) @@ -149,6 +191,10 @@ bool CASW_Steamstats::FetchStats( CSteamID playerSteamID, CASW_Player *pPlayer ) m_DifficultyCounts.Purge(); m_WeaponStats.Purge(); + // Returns true so we don't re-fetch stats + if( !IsOfficialCampaign() ) + return true; + // Fetch the player's overall stats FETCH_STEAM_STATS( "iTotalKills", m_iTotalKills ); FETCH_STEAM_STATS( "fAccuracy", m_fAccuracy ); @@ -253,7 +299,7 @@ bool CASW_Steamstats::FetchStats( CSteamID playerSteamID, CASW_Player *pPlayer ) } // Get difficulty counts - for( int i=0; i < 4; ++i ) + for( int i=0; i < 5; ++i ) { int32 iTempCount; FETCH_STEAM_STATS( CFmtStr( "%s.games.total", g_szDifficulties[ i ] ), iTempCount ); @@ -275,7 +321,13 @@ void CASW_Steamstats::PrepStatsForSend( CASW_Player *pPlayer ) return; // Update stats from the briefing screen - if( !GetDebriefStats() || !ASWGameResource() ) + if( !GetDebriefStats() + || !ASWGameResource() + || !IsOfficialCampaign() +#ifndef DEBUG + || ASWGameRules()->m_bCheated +#endif + ) return; if( m_MarineSelectionCounts.Count() == 0 || @@ -386,7 +438,10 @@ void CASW_Steamstats::PrepStatsForSend( CASW_Player *pPlayer ) SEND_STEAM_STATS( CFmtStr( "marines.%i.total", iMarineProfileIndex ), m_MarineSelectionCounts[iMarineProfileIndex] ); int iLevel = pPlayer->GetLevel(); SEND_STEAM_STATS( "level", iLevel ); - SEND_STEAM_STATS( "level.xprequired", ( iLevel == NELEMS( g_iLevelExperience ) ) ? 0 : g_iLevelExperience[ iLevel ] ); + int iPromotion = pPlayer->GetPromotion(); + float flXPRequired = ( iLevel == NELEMS( g_iLevelExperience ) ) ? 0 : g_iLevelExperience[ iLevel ]; + flXPRequired *= g_flPromotionXPScale[ iPromotion ]; + SEND_STEAM_STATS( "level.xprequired", (int) flXPRequired ); // Send favorite equip info SEND_STEAM_STATS( "equips.primary.fav", GetFavoriteEquip(0) ); @@ -560,6 +615,8 @@ bool DifficultyStats_t::FetchDifficultyStats( CSteamAPIContext * pSteamContext, break; case 4: szDifficulty = "insane"; break; + case 5: szDifficulty = "imba"; + break; } if( szDifficulty ) { @@ -618,6 +675,8 @@ void DifficultyStats_t::PrepStatsForSend( CASW_Player *pPlayer ) break; case 4: szDifficulty = "insane"; break; + case 5: szDifficulty = "imba"; + break; } if( szDifficulty ) { @@ -652,6 +711,12 @@ bool MissionStats_t::FetchMissionStats( CSteamAPIContext * pSteamContext, CSteam FETCH_STEAM_STATS( CFmtStr( "%s%s", szLevelName, szKillsTotal ), m_iKillsTotal ); FETCH_STEAM_STATS( CFmtStr( "%s%s", szLevelName, szDamageTotal ), m_iDamageTotal ); FETCH_STEAM_STATS( CFmtStr( "%s%s", szLevelName, szFFTotal ), m_iFFTotal ); + FETCH_STEAM_STATS( CFmtStr( "%s%s", szLevelName, szTimeTotal ), m_iTimeTotal ); + FETCH_STEAM_STATS( CFmtStr( "%s%s", szLevelName, szBestDifficulty ), m_iHighestDifficulty ); + for( int i=0; i<5; ++i ) + { + FETCH_STEAM_STATS( CFmtStr( "%s%s.%s", szLevelName, szBestTime, g_szDifficulties[i] ), m_iBestSpeedrunTimes[i] ); + } return bOK; } @@ -663,6 +728,7 @@ void MissionStats_t::PrepStatsForSend( CASW_Player *pPlayer ) return; CASW_Marine_Resource *pMR = ASWGameResource()->GetFirstMarineResourceForPlayer( pPlayer ); + int iDifficulty = ASWGameRules()->GetSkillLevel(); if ( pMR ) { int iMarineIndex = ASWGameResource()->GetMarineResourceIndex( pMR ); @@ -674,6 +740,24 @@ void MissionStats_t::PrepStatsForSend( CASW_Player *pPlayer ) m_iGamesTotal++; m_iGamesSuccess += ASWGameRules()->GetMissionSuccess() ? 1 : 0; m_fGamesSuccessPercent = m_iGamesSuccess / (float)m_iGamesTotal * 100.0f; + m_iTimeTotal += GetDebriefStats()->m_fTimeTaken; + if( ASWGameRules()->GetMissionSuccess() ) + { + if( iDifficulty > m_iHighestDifficulty ) + m_iHighestDifficulty = iDifficulty; + + if( (unsigned int)m_iBestSpeedrunTimes[ iDifficulty - 1 ] > GetDebriefStats()->m_fTimeTaken ) + { + m_iBestSpeedrunTimes[ iDifficulty - 1 ] = GetDebriefStats()->m_fTimeTaken; + } + } + + + // Safely compute averages + m_fKillsAvg = m_iKillsTotal / (float)m_iGamesTotal; + m_fFFAvg = m_iFFTotal / (float)m_iGamesTotal; + m_fDamageAvg = m_iDamageTotal / (float)m_iGamesTotal; + m_iTimeAvg = m_iTimeTotal / (float)m_iGamesTotal; } } @@ -692,7 +776,13 @@ void MissionStats_t::PrepStatsForSend( CASW_Player *pPlayer ) SEND_STEAM_STATS( CFmtStr( "%s%s", szLevelName, szKillsTotal ), m_iKillsTotal ); SEND_STEAM_STATS( CFmtStr( "%s%s", szLevelName, szDamageTotal ), m_iDamageTotal ); SEND_STEAM_STATS( CFmtStr( "%s%s", szLevelName, szFFTotal ), m_iFFTotal ); - + SEND_STEAM_STATS( CFmtStr( "%s%s", szLevelName, szTimeTotal ), m_iTimeTotal ); + SEND_STEAM_STATS( CFmtStr( "%s%s", szLevelName, szKillsAvg ), m_fKillsAvg ); + SEND_STEAM_STATS( CFmtStr( "%s%s", szLevelName, szDamageAvg ), m_fDamageAvg ); + SEND_STEAM_STATS( CFmtStr( "%s%s", szLevelName, szFFAvg ), m_fFFAvg ); + SEND_STEAM_STATS( CFmtStr( "%s%s", szLevelName, szTimeAvg ), m_iTimeAvg ); + SEND_STEAM_STATS( CFmtStr( "%s%s", szLevelName, szBestDifficulty ), m_iHighestDifficulty ); + SEND_STEAM_STATS( CFmtStr( "%s%s.%s", szLevelName, szBestTime, g_szDifficulties[ iDifficulty - 1 ] ), m_iBestSpeedrunTimes[ iDifficulty - 1 ] ); } bool WeaponStats_t::FetchWeaponStats( CSteamAPIContext * pSteamContext, CSteamID playerSteamID, const char *szClassName ) diff --git a/sdk_src/game/client/swarm/c_asw_steamstats.h b/sdk_src/game/client/swarm/c_asw_steamstats.h index 144955e..7bcde65 100644 --- a/sdk_src/game/client/swarm/c_asw_steamstats.h +++ b/sdk_src/game/client/swarm/c_asw_steamstats.h @@ -34,9 +34,13 @@ struct MissionStats_t int32 m_iKillsTotal; int32 m_iDamageTotal; int32 m_iFFTotal; - int32 m_iKillsAvg; - int32 m_iDamageAvg; - int32 m_iFFAvg; + float32 m_fKillsAvg; + float32 m_fDamageAvg; + float32 m_fFFAvg; + int32 m_iTimeTotal; + int32 m_iTimeAvg; + int32 m_iHighestDifficulty; + int32 m_iBestSpeedrunTimes[5]; }; struct WeaponStats_t @@ -105,7 +109,7 @@ private: StatList_Int_t m_MarineSelectionCounts; StatList_Int_t m_DifficultyCounts; - DifficultyStats_t m_DifficultyStats[4]; + DifficultyStats_t m_DifficultyStats[5]; MissionStats_t m_MissionStats; WeaponStatList_t m_WeaponStats; diff --git a/sdk_src/game/client/swarm/gameui/swarm/vfooterpanel.h b/sdk_src/game/client/swarm/gameui/swarm/vfooterpanel.h index a94d43a..ae6e22d 100644 --- a/sdk_src/game/client/swarm/gameui/swarm/vfooterpanel.h +++ b/sdk_src/game/client/swarm/gameui/swarm/vfooterpanel.h @@ -58,6 +58,7 @@ public: FooterFormat_t GetFormat(); bool GetHelpTextEnabled(); void SetHelpText( const char *text ); + const char * GetHelpText() { return m_HelpText; } void FadeHelpText( void ); void GetPosition( int &x, int &y ); bool HasContent( void ); diff --git a/sdk_src/game/client/swarm/gameui/swarm/vfoundgames.cpp b/sdk_src/game/client/swarm/gameui/swarm/vfoundgames.cpp index 9d55eb4..e77a2db 100644 --- a/sdk_src/game/client/swarm/gameui/swarm/vfoundgames.cpp +++ b/sdk_src/game/client/swarm/gameui/swarm/vfoundgames.cpp @@ -966,7 +966,7 @@ FoundGames::FoundGames( Panel *parent, const char *panelName ): m_pHeaderFooter->SetHeaderEnabled( false ); m_pHeaderFooter->SetFooterEnabled( true ); m_pHeaderFooter->SetGradientBarEnabled( true ); - m_pHeaderFooter->SetGradientBarPos( 80, 300 ); + m_pHeaderFooter->SetGradientBarPos( 80, 315 ); m_pTitle = new vgui::Label( this, "Title", "" ); diff --git a/sdk_src/game/client/swarm/gameui/swarm/vfoundpublicgames.cpp b/sdk_src/game/client/swarm/gameui/swarm/vfoundpublicgames.cpp index 55b6969..e6d6c47 100644 --- a/sdk_src/game/client/swarm/gameui/swarm/vfoundpublicgames.cpp +++ b/sdk_src/game/client/swarm/gameui/swarm/vfoundpublicgames.cpp @@ -29,6 +29,7 @@ using namespace BaseModUI; //============================================================================= static ConVar ui_public_lobby_filter_difficulty2( "ui_public_lobby_filter_difficulty2", "", FCVAR_ARCHIVE, "Filter type for difficulty on the public lobby display" ); +static ConVar ui_public_lobby_filter_onslaught( "ui_public_lobby_filter_onslaught", "", FCVAR_ARCHIVE, "Filter type for Onslaught mode on the public lobby display"); ConVar ui_public_lobby_filter_campaign( "ui_public_lobby_filter_campaign", "", FCVAR_ARCHIVE, "Filter type for campaigns on the public lobby display" ); ConVar ui_public_lobby_filter_status( "ui_public_lobby_filter_status", "", FCVAR_ARCHIVE, "Filter type for game status on the public lobby display" ); @@ -38,6 +39,7 @@ FoundPublicGames::FoundPublicGames( Panel *parent, const char *panelName ) : m_pSearchManager( NULL ) { m_drpDifficulty = NULL; + m_drpOnslaught = NULL; m_drpGameStatus = NULL; m_drpCampaign = NULL; @@ -92,6 +94,7 @@ void FoundPublicGames::ApplySchemeSettings( IScheme *pScheme ) BaseClass::ApplySchemeSettings( pScheme ); m_drpDifficulty = dynamic_cast< DropDownMenu* >( FindChildByName( "DrpFilterDifficulty" ) ); + m_drpOnslaught = dynamic_cast< DropDownMenu* >( FindChildByName( "DrpFilterOnslaught" ) ); m_drpGameStatus = dynamic_cast< DropDownMenu* >( FindChildByName( "DrpFilterGameStatus" ) ); m_drpCampaign = dynamic_cast< DropDownMenu* >( FindChildByName( "DrpFilterCampaign" ) ); m_btnFilters = dynamic_cast< BaseModUI::BaseModHybridButton* >( FindChildByName( "BtnFilters" ) ); @@ -168,6 +171,10 @@ void FoundPublicGames::StartSearching( void ) if ( szDifficulty && *szDifficulty && GameModeHasDifficulty( szGameMode ) ) pKeyValuesSearch->SetString( "game/difficulty", szDifficulty ); + char const *szOnslaught = ui_public_lobby_filter_onslaught.GetString(); + if ( szOnslaught && *szOnslaught ) + pKeyValuesSearch->SetInt( "game/onslaught", 1 ); + char const *szStatus = ui_public_lobby_filter_status.GetString(); if ( szStatus && *szStatus ) pKeyValuesSearch->SetString( "game/state", szStatus ); @@ -501,6 +508,11 @@ void FoundPublicGames::OnCommand( const char *command ) ui_public_lobby_filter_difficulty2.SetValue( filterDifficulty ); StartSearching(); } + else if ( char const *filterOnslaught = StringAfterPrefix( command, "filter_onslaught_" ) ) + { + ui_public_lobby_filter_onslaught.SetValue( filterOnslaught ); + StartSearching(); + } else if ( char const *filterCampaign = StringAfterPrefix( command, "filter_campaign_" ) ) { ui_public_lobby_filter_campaign.SetValue( filterCampaign ); @@ -544,6 +556,11 @@ void FoundPublicGames::Activate() m_drpDifficulty->SetCurrentSelection( CFmtStr( "filter_difficulty_%s", ui_public_lobby_filter_difficulty2.GetString() ) ); } + if ( m_drpOnslaught ) + { + m_drpOnslaught->SetCurrentSelection( CFmtStr( "filter_onslaught_%s", ui_public_lobby_filter_onslaught.GetString() ) ); + } + if ( m_drpGameStatus ) { m_drpGameStatus->SetCurrentSelection( CFmtStr( "filter_status_%s", ui_public_lobby_filter_status.GetString() ) ); diff --git a/sdk_src/game/client/swarm/gameui/swarm/vfoundpublicgames.h b/sdk_src/game/client/swarm/gameui/swarm/vfoundpublicgames.h index f5fb080..f25bdd0 100644 --- a/sdk_src/game/client/swarm/gameui/swarm/vfoundpublicgames.h +++ b/sdk_src/game/client/swarm/gameui/swarm/vfoundpublicgames.h @@ -55,6 +55,7 @@ namespace BaseModUI { #endif DropDownMenu* m_drpDifficulty; + DropDownMenu* m_drpOnslaught; DropDownMenu* m_drpGameStatus; DropDownMenu* m_drpCampaign; BaseModUI::BaseModHybridButton *m_btnFilters; diff --git a/sdk_src/game/client/swarm/gameui/swarm/vgamesettings.cpp b/sdk_src/game/client/swarm/gameui/swarm/vgamesettings.cpp index 2b4fe2b..03a3e4d 100644 --- a/sdk_src/game/client/swarm/gameui/swarm/vgamesettings.cpp +++ b/sdk_src/game/client/swarm/gameui/swarm/vgamesettings.cpp @@ -55,13 +55,15 @@ GameSettings::GameSettings( vgui::Panel *parent, const char *panelName ): m_drpStartingMission( NULL ), m_bEditingSession( false ), m_bAllowChangeToCustomCampaign( true ), - m_bPreventSessionModifications( false ) + m_bPreventSessionModifications( false ), + m_drpFriendlyFire( NULL ), + m_drpOnslaught( NULL ) { m_pHeaderFooter = new CNB_Header_Footer( this, "HeaderFooter" ); m_pHeaderFooter->SetTitle( "" ); m_pHeaderFooter->SetHeaderEnabled( false ); m_pHeaderFooter->SetGradientBarEnabled( true ); - m_pHeaderFooter->SetGradientBarPos( 150, 170 ); + m_pHeaderFooter->SetGradientBarPos( 140, 190 ); m_pTitle = new vgui::Label( this, "Title", "" ); SetDeleteSelfOnClose(true); SetProportional( true ); @@ -206,6 +208,36 @@ void GameSettings::Activate() flyout->CloseMenu( NULL ); } + if ( m_drpFriendlyFire ) + { + if ( m_pSettings->GetInt( "game/hardcoreFF", 0 ) == 1 ) + { + m_drpFriendlyFire->SetCurrentSelection( "#L4D360UI_HardcoreFF" ); + } + else + { + m_drpFriendlyFire->SetCurrentSelection( "#L4D360UI_RegularFF" ); + } + + if ( FlyoutMenu* flyout = m_drpFriendlyFire->GetCurrentFlyout() ) + flyout->CloseMenu( NULL ); + } + + if ( m_drpOnslaught ) + { + if ( m_pSettings->GetInt( "game/onslaught", 0 ) == 1 ) + { + m_drpOnslaught->SetCurrentSelection( "#L4D360UI_OnslaughtEnabled" ); + } + else + { + m_drpOnslaught->SetCurrentSelection( "#L4D360UI_OnslaughtDisabled" ); + } + + if ( FlyoutMenu* flyout = m_drpOnslaught->GetCurrentFlyout() ) + flyout->CloseMenu( NULL ); + } + // If we have an active control, navigate from it since we'll be setting a new one if ( m_ActiveControl ) { @@ -581,6 +613,94 @@ void GameSettings::OnCommand(const char *command) pFlyout->SetListener( this ); } } + else if ( !Q_strcmp( command, "#L4D360UI_RegularFF" ) ) + { + KeyValues *pSettings = KeyValues::FromString( + "update", + " update { " + " game { " + " hardcoreFF = " + " } " + " } " + ); + KeyValues::AutoDelete autodelete( pSettings ); + + pSettings->SetInt( "update/game/hardcoreFF", 0 ); + + UpdateSessionSettings( pSettings ); + + if( m_drpFriendlyFire ) + { + if ( FlyoutMenu* pFlyout = m_drpFriendlyFire->GetCurrentFlyout() ) + pFlyout->SetListener( this ); + } + } + else if ( !Q_strcmp( command, "#L4D360UI_HardcoreFF" ) ) + { + KeyValues *pSettings = KeyValues::FromString( + "update", + " update { " + " game { " + " hardcoreFF = " + " } " + " } " + ); + KeyValues::AutoDelete autodelete( pSettings ); + + pSettings->SetInt( "update/game/hardcoreFF", 1 ); + + UpdateSessionSettings( pSettings ); + + if( m_drpFriendlyFire ) + { + if ( FlyoutMenu* pFlyout = m_drpFriendlyFire->GetCurrentFlyout() ) + pFlyout->SetListener( this ); + } + } + else if ( !Q_strcmp( command, "#L4D360UI_OnslaughtDisabled" ) ) + { + KeyValues *pSettings = KeyValues::FromString( + "update", + " update { " + " game { " + " onslaught = " + " } " + " } " + ); + KeyValues::AutoDelete autodelete( pSettings ); + + pSettings->SetInt( "update/game/onslaught", 0 ); + + UpdateSessionSettings( pSettings ); + + if( m_drpOnslaught ) + { + if ( FlyoutMenu* pFlyout = m_drpOnslaught->GetCurrentFlyout() ) + pFlyout->SetListener( this ); + } + } + else if ( !Q_strcmp( command, "#L4D360UI_OnslaughtEnabled" ) ) + { + KeyValues *pSettings = KeyValues::FromString( + "update", + " update { " + " game { " + " onslaught = " + " } " + " } " + ); + KeyValues::AutoDelete autodelete( pSettings ); + + pSettings->SetInt( "update/game/onslaught", 1 ); + + UpdateSessionSettings( pSettings ); + + if( m_drpOnslaught ) + { + if ( FlyoutMenu* pFlyout = m_drpOnslaught->GetCurrentFlyout() ) + pFlyout->SetListener( this ); + } + } else if ( const char *szRoundLimitValue = StringAfterPrefix( command, "#L4D360UI_RoundLimit_" ) ) { KeyValues *pSettings = new KeyValues( "update" ); @@ -654,6 +774,8 @@ void GameSettings::ApplySchemeSettings( vgui::IScheme *pScheme ) m_drpDifficulty = dynamic_cast< DropDownMenu* >( FindChildByName( "DrpDifficulty" ) ); m_drpGameType = dynamic_cast< DropDownMenu* >( FindChildByName( "DrpGameType" ) ); + m_drpFriendlyFire = dynamic_cast< DropDownMenu* >( FindChildByName( "DrpFriendlyFire" ) ); + m_drpOnslaught = dynamic_cast< DropDownMenu* >( FindChildByName( "DrpOnslaught" ) ); m_drpGameAccess = dynamic_cast< DropDownMenu* >( FindChildByName( "DrpGameAccess" ) ); if ( m_drpGameAccess ) @@ -710,6 +832,12 @@ void GameSettings::OnClose() if( m_drpGameType ) m_drpGameType->CloseDropDown(); + if( m_drpFriendlyFire ) + m_drpFriendlyFire->CloseDropDown(); + + if( m_drpOnslaught ) + m_drpOnslaught->CloseDropDown(); + m_pSettings = NULL; // NULL out settings in case we get some calls // after we are closed if ( m_bCloseSessionOnClose ) diff --git a/sdk_src/game/client/swarm/gameui/swarm/vgamesettings.h b/sdk_src/game/client/swarm/gameui/swarm/vgamesettings.h index 638d1c8..4fab632 100644 --- a/sdk_src/game/client/swarm/gameui/swarm/vgamesettings.h +++ b/sdk_src/game/client/swarm/gameui/swarm/vgamesettings.h @@ -73,6 +73,8 @@ private: DropDownMenu* m_drpGameAccess; DropDownMenu* m_drpServerType; DropDownMenu* m_drpStartingMission; + DropDownMenu* m_drpFriendlyFire; + DropDownMenu* m_drpOnslaught; CNB_Header_Footer *m_pHeaderFooter; vgui::Label *m_pTitle; diff --git a/sdk_src/game/client/swarm/gameui/swarm/vingamedifficultyselect.cpp b/sdk_src/game/client/swarm/gameui/swarm/vingamedifficultyselect.cpp index 0e800fc..fe709bf 100644 --- a/sdk_src/game/client/swarm/gameui/swarm/vingamedifficultyselect.cpp +++ b/sdk_src/game/client/swarm/gameui/swarm/vingamedifficultyselect.cpp @@ -92,7 +92,8 @@ void InGameDifficultySelect::OnCommand(const char *command) if ( !Q_strcmp( command, "Easy" ) || !Q_strcmp( command, "Normal" ) || !Q_strcmp( command, "Hard" ) || - !Q_strcmp( command, "Insane" ) ) + !Q_strcmp( command, "Insane" ) || + !Q_strcmp( command, "Imba" ) ) { CGameUIConVarRef z_difficulty("z_difficulty"); diff --git a/sdk_src/game/client/swarm/vgui/asw_difficulty_chooser.cpp b/sdk_src/game/client/swarm/vgui/asw_difficulty_chooser.cpp index 1382467..c1f6d79 100644 --- a/sdk_src/game/client/swarm/vgui/asw_difficulty_chooser.cpp +++ b/sdk_src/game/client/swarm/vgui/asw_difficulty_chooser.cpp @@ -140,6 +140,7 @@ CASW_Difficulty_Entry::CASW_Difficulty_Entry( vgui::Panel *pParent, const char * case 2: m_pDifficultyLabel->SetText("#asw_difficulty_chooser_normal"); break; case 3: m_pDifficultyLabel->SetText("#asw_difficulty_chooser_hard"); break; case 4: m_pDifficultyLabel->SetText("#asw_difficulty_chooser_insane"); break; + case 5: m_pDifficultyLabel->SetText("#asw_difficulty_chooser_imba"); break; default: m_pDifficultyLabel->SetText("???"); break; } @@ -149,6 +150,7 @@ CASW_Difficulty_Entry::CASW_Difficulty_Entry( vgui::Panel *pParent, const char * case 2: m_pDifficultyDescriptionLabel->SetText("#asw_difficulty_chooser_normald"); break; case 3: m_pDifficultyDescriptionLabel->SetText("#asw_difficulty_chooser_hardd"); break; case 4: m_pDifficultyDescriptionLabel->SetText("#asw_difficulty_chooser_insaned"); break; + case 5: m_pDifficultyDescriptionLabel->SetText("#asw_difficulty_chooser_imbad"); break; default: m_pDifficultyDescriptionLabel->SetText("???"); break; } @@ -158,6 +160,7 @@ CASW_Difficulty_Entry::CASW_Difficulty_Entry( vgui::Panel *pParent, const char * case 2: m_pImagePanel->SetImage("swarm/MissionPics/DifficultyPicNormal"); break; case 3: m_pImagePanel->SetImage("swarm/MissionPics/DifficultyPicHard"); break; case 4: m_pImagePanel->SetImage("swarm/MissionPics/DifficultyPicInsane"); break; + case 5: m_pImagePanel->SetImage("swarm/MissionPics/DifficultyPicInsane"); break; default: m_pImagePanel->SetImage("swarm/MissionPics/UnknownMissionPic"); break; } diff --git a/sdk_src/game/client/swarm/vgui/experience_bar.cpp b/sdk_src/game/client/swarm/vgui/experience_bar.cpp index 72b4a50..cdba468 100644 --- a/sdk_src/game/client/swarm/vgui/experience_bar.cpp +++ b/sdk_src/game/client/swarm/vgui/experience_bar.cpp @@ -40,17 +40,28 @@ ExperienceBar::ExperienceBar(vgui::Panel *parent, const char *name) : m_pExperienceBar->SetShowMaxOnCounter( true ); m_pExperienceBar->SetColors( Color( 255, 255, 255, 0 ), Color( 93,148,192,255 ), Color( 255, 255, 255, 255 ), Color( 17,37,57,255 ), Color( 35, 77, 111, 255 ) ); //m_pExperienceBar->m_bShowCumulativeTotal = true; - m_pExperienceBar->AddMinMax( 0, g_iLevelExperience[ 0 ] ); - for ( int i = 0; i < ASW_NUM_EXPERIENCE_LEVELS - 1; i++ ) - { - m_pExperienceBar->AddMinMax( g_iLevelExperience[ i ], g_iLevelExperience[ i + 1 ] ); - } + m_nLastPromotion = -1; + UpdateMinMaxes( 0 ); m_pExperienceBar->m_flBorder = 1.5f; vgui::ivgui()->AddTickSignal( GetVPanel() ); } +void ExperienceBar::UpdateMinMaxes( int nPromotion ) +{ + if ( m_nLastPromotion == nPromotion ) + return; + + m_nLastPromotion = nPromotion; + m_pExperienceBar->ClearMinMax(); + m_pExperienceBar->AddMinMax( 0, g_iLevelExperience[ 0 ] * g_flPromotionXPScale[ m_nLastPromotion ] ); + for ( int i = 0; i < ASW_NUM_EXPERIENCE_LEVELS - 1; i++ ) + { + m_pExperienceBar->AddMinMax( g_iLevelExperience[ i ] * g_flPromotionXPScale[ m_nLastPromotion ] , g_iLevelExperience[ i + 1 ] * g_flPromotionXPScale[ m_nLastPromotion ] ); + } +} + void ExperienceBar::ApplySchemeSettings( vgui::IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); @@ -97,9 +108,9 @@ void ExperienceBar::OnTick() } } - if ( ASWGameRules()->GetGameState() <= ASW_GS_BRIEFING ) + if ( m_hPlayer.Get() ) { - if ( m_hPlayer.Get() ) + if ( ASWGameRules()->GetGameState() <= ASW_GS_BRIEFING ) { int nXP = m_hPlayer->GetExperience(); if ( nXP != m_nOldPlayerXP ) @@ -114,32 +125,32 @@ void ExperienceBar::OnTick() m_pPlayerNameLabel->SetText( m_hPlayer->GetPlayerName() ); } - } - else - { - float flBarMin = m_pExperienceBar->GetBarMin(); - bool bCapped = ( (int) m_pExperienceBar->m_fCurrent ) == ASW_XP_CAP; - - if ( m_flOldBarMin == -1 ) + else { + float flBarMin = m_pExperienceBar->GetBarMin(); + bool bCapped = ( (int) m_pExperienceBar->m_fCurrent ) >= ASW_XP_CAP * g_flPromotionXPScale[ m_hPlayer->GetPromotion() ]; + + if ( m_flOldBarMin == -1 ) + { + m_bOldCapped = bCapped; + } + + if ( m_flOldBarMin != -1 && ( m_flOldBarMin != flBarMin || m_bOldCapped != bCapped ) ) // bar min has changed - player has levelled up! + { + m_iPlayerLevel = LevelFromXP( m_pExperienceBar->m_fCurrent, m_hPlayer->GetPromotion() ); + UpdateLevelLabel(); + + m_pLevelUpLabel->SetVisible( true ); + SkillAnimPanel *pSkillAnim = dynamic_cast(GetClientMode()->GetViewport()->FindChildByName("SkillAnimPanel", true)); + if ( pSkillAnim ) + { + pSkillAnim->AddParticlesAroundPanel( m_pPlayerLevelLabel ); + } + } + + m_flOldBarMin = flBarMin; m_bOldCapped = bCapped; } - - if ( m_flOldBarMin != -1 && ( m_flOldBarMin != flBarMin || m_bOldCapped != bCapped ) ) // bar min has changed - player has levelled up! - { - m_iPlayerLevel = LevelFromXP( m_pExperienceBar->m_fCurrent ); - UpdateLevelLabel(); - - m_pLevelUpLabel->SetVisible( true ); - SkillAnimPanel *pSkillAnim = dynamic_cast(GetClientMode()->GetViewport()->FindChildByName("SkillAnimPanel", true)); - if ( pSkillAnim ) - { - pSkillAnim->AddParticlesAroundPanel( m_pPlayerLevelLabel ); - } - } - - m_flOldBarMin = flBarMin; - m_bOldCapped = bCapped; } } @@ -157,11 +168,6 @@ void ExperienceBar::InitFor( C_ASW_Player *pPlayer ) m_pPlayerNameLabel->SetText( "Player" ); m_pPlayerLevelLabel->SetText( "Level 5" ); m_pExperienceBar->Init( 1200, 1500, 1500.0f / 4.0f, true, false ); - m_pExperienceBar->AddMinMax( 0, g_iLevelExperience[ 0 ] ); - for ( int i = 0; i < ASW_NUM_EXPERIENCE_LEVELS - 1; i++ ) - { - m_pExperienceBar->AddMinMax( g_iLevelExperience[ i ], g_iLevelExperience[ i + 1 ] ); - } m_pExperienceBar->SetStartCountingTime( gpGlobals->curtime + 15.0f ); } return; @@ -188,6 +194,7 @@ void ExperienceBar::InitFor( C_ASW_Player *pPlayer ) } #endif + UpdateMinMaxes( pPlayer->GetPromotion() ); if ( ASWGameRules()->GetGameState() <= ASW_GS_BRIEFING ) { m_iPlayerLevel = pPlayer->GetLevel(); @@ -204,7 +211,7 @@ void ExperienceBar::InitFor( C_ASW_Player *pPlayer ) int iEarnedXP = pPlayer->GetEarnedXP( ASW_XP_TOTAL ); int nGoalXP = pPlayer->GetExperienceBeforeDebrief() + iEarnedXP; - nGoalXP = MIN( nGoalXP, ASW_XP_CAP ); + nGoalXP = MIN( nGoalXP, ASW_XP_CAP * g_flPromotionXPScale[ pPlayer->GetPromotion() ] ); float flRate = (float) iEarnedXP / 3.0f; // take 4 seconds to increase XP. if ( iEarnedXP < 150 ) // if XP is really low, count it up in 1 second { diff --git a/sdk_src/game/client/swarm/vgui/experience_bar.h b/sdk_src/game/client/swarm/vgui/experience_bar.h index 0337d19..13390fa 100644 --- a/sdk_src/game/client/swarm/vgui/experience_bar.h +++ b/sdk_src/game/client/swarm/vgui/experience_bar.h @@ -32,6 +32,7 @@ public: void InitFor( C_ASW_Player *pPlayer ); void UpdateLevelLabel(); + void UpdateMinMaxes( int nPromotion ); bool IsDoneAnimating(); @@ -51,6 +52,7 @@ public: int m_iPlayerLevel; int m_nOldPlayerXP; CSteamID m_lastSteamID; + int m_nLastPromotion; }; class ExperienceBarSmall : public ExperienceBar diff --git a/sdk_src/game/client/swarm/vgui/experience_report.cpp b/sdk_src/game/client/swarm/vgui/experience_report.cpp index 5418b70..2811f7b 100644 --- a/sdk_src/game/client/swarm/vgui/experience_report.cpp +++ b/sdk_src/game/client/swarm/vgui/experience_report.cpp @@ -161,7 +161,7 @@ void CExperienceReport::PerformLayout() if ( pPlayer ) { int nPlayerLevel = pPlayer->GetLevel(); - m_pWeaponUnlockPanel->SetVisible( nPlayerLevel < ASW_LEVEL_CAP ); + m_pWeaponUnlockPanel->SetVisible( nPlayerLevel < ASW_NUM_EXPERIENCE_LEVELS ); if ( ASWGameRules() && ASWGameRules()->m_iSkillLevel == 2 && !ASWGameRules()->IsOfflineGame() && !( ASWGameRules()->IsCampaignGame() && ASWGameRules()->CampaignMissionsLeft() <= 1 ) ) @@ -194,26 +194,28 @@ void CExperienceReport::OnThink() // monitor local player's experience bar to see when it loops float flBarMin = m_pExperienceBar[ 0 ]->m_pExperienceBar->GetBarMin(); - bool bCapped = ( (int) m_pExperienceBar[ 0 ]->m_pExperienceBar->m_fCurrent ) == ASW_XP_CAP; - - if ( m_flOldBarMin == -1 ) + bool bCapped = false; + + C_ASW_Player *pPlayer = C_ASW_Player::GetLocalASWPlayer(); + if ( pPlayer ) { - m_bOldCapped = bCapped; - } - - if ( m_flOldBarMin != -1 && ( m_flOldBarMin != flBarMin || m_bOldCapped != bCapped ) ) // bar min has changed - player has levelled up! - { - m_bPendingUnlockSequence = true; - IGameEvent *event = gameeventmanager->CreateEvent( "level_up" ); - int nNewLevel = LevelFromXP( (int) m_pExperienceBar[ 0 ]->m_pExperienceBar->m_fCurrent ); - if ( event ) + bCapped = ( (int) m_pExperienceBar[ 0 ]->m_pExperienceBar->m_fCurrent ) >= ASW_XP_CAP * g_flPromotionXPScale[ pPlayer->GetPromotion() ]; + if ( m_flOldBarMin == -1 ) { - event->SetInt( "level", nNewLevel ); - gameeventmanager->FireEventClientSide( event ); + m_bOldCapped = bCapped; } - C_ASW_Player *pPlayer = C_ASW_Player::GetLocalASWPlayer(); - if ( pPlayer ) + + if ( m_flOldBarMin != -1 && ( m_flOldBarMin != flBarMin || m_bOldCapped != bCapped ) ) // bar min has changed - player has levelled up! { + m_bPendingUnlockSequence = true; + IGameEvent *event = gameeventmanager->CreateEvent( "level_up" ); + int nNewLevel = LevelFromXP( (int) m_pExperienceBar[ 0 ]->m_pExperienceBar->m_fCurrent, pPlayer->GetPromotion() ); + if ( event ) + { + event->SetInt( "level", nNewLevel ); + gameeventmanager->FireEventClientSide( event ); + } + const char *szWeaponClassUnlocked = pPlayer->GetWeaponUnlockedAtLevel( nNewLevel ); if ( szWeaponClassUnlocked ) { @@ -227,9 +229,10 @@ void CExperienceReport::OnThink() pComplete->OnWeaponUnlocked( szWeaponClassUnlocked ); } } + + CLocalPlayerFilter filter; + C_BaseEntity::EmitSound( filter, -1 /*SOUND_FROM_LOCAL_PLAYER*/, "ASW_XP.LevelUp" ); } - CLocalPlayerFilter filter; - C_BaseEntity::EmitSound( filter, -1 /*SOUND_FROM_LOCAL_PLAYER*/, "ASW_XP.LevelUp" ); } m_flOldBarMin = flBarMin; @@ -256,6 +259,7 @@ void CExperienceReport::OnThink() case 2: m_pXPDifficultyScaleNumber->SetText( "" ); flTotalXP *= g_flXPDifficultyScale[1]; break; case 3: m_pXPDifficultyScaleNumber->SetText( "+20%" ); flTotalXP *= g_flXPDifficultyScale[2]; break; case 4: m_pXPDifficultyScaleNumber->SetText( "+40%" ); flTotalXP *= g_flXPDifficultyScale[3]; break; + case 5: m_pXPDifficultyScaleNumber->SetText( "+50%" ); flTotalXP *= g_flXPDifficultyScale[4]; break; } bool bShowDifficultyBonus = ( ASWGameRules()->GetSkillLevel() != 2 ); m_pXPDifficultyScaleNumber->SetVisible( bShowDifficultyBonus ); diff --git a/sdk_src/game/client/swarm/vgui/missionstatspanel.cpp b/sdk_src/game/client/swarm/vgui/missionstatspanel.cpp index 4f3ac30..2452dd2 100644 --- a/sdk_src/game/client/swarm/vgui/missionstatspanel.cpp +++ b/sdk_src/game/client/swarm/vgui/missionstatspanel.cpp @@ -115,29 +115,25 @@ void MissionStatsPanel::SetMissionLabels(vgui::Label *pMissionLabel, vgui::Label pszToken = "#asw_difficulty_easy"; else if (iDiff == 3) pszToken = "#asw_difficulty_hard"; - else if (iDiff >= 4) + else if (iDiff == 4) pszToken = "#asw_difficulty_insane"; + else if (iDiff >= 5) + pszToken = "#asw_difficulty_imba"; const wchar_t *pDiff = g_pVGuiLocalize->Find( pszToken ); - // find campaign/single mission - if (bCampaign) - pszToken = "#asw_difficulty_campaign"; - else - pszToken = "#asw_difficulty_mission"; - const wchar_t *pCampaign = g_pVGuiLocalize->Find( pszToken ); + bool bOnslaught = CAlienSwarm::IsOnslaught(); - // find style - if (bUber) - pszToken = "#asw_difficulty_uber"; - else if (bCarnage) - pszToken = "#asw_difficulty_carnage"; - else if (bHardcore) - pszToken = "#asw_difficulty_hardcore"; - - const wchar_t *pStyle = L""; - if (bUber || bCarnage || bHardcore) + const wchar_t *pOnslaught = L""; + if ( bOnslaught ) { - pStyle = g_pVGuiLocalize->Find( pszToken ); + pOnslaught = g_pVGuiLocalize->Find( "#nb_onslaught_title" ); + } + + bool bHardcoreFriendlyFire = CAlienSwarm::IsHardcoreFF(); + const wchar_t *pHardcoreFF = L""; + if ( bHardcoreFriendlyFire ) + { + pHardcoreFF = g_pVGuiLocalize->Find( "#asw_hardcore_ff" ); } const wchar_t *pCheated = L""; @@ -149,7 +145,7 @@ void MissionStatsPanel::SetMissionLabels(vgui::Label *pMissionLabel, vgui::Label wchar_t mission_difficulty[96]; g_pVGuiLocalize->ConstructString( mission_difficulty, sizeof(mission_difficulty), g_pVGuiLocalize->Find("#asw_mission_difficulty"), 4, - pCampaign, pDiff, pStyle, pCheated); + pDiff, pOnslaught, pHardcoreFF, pCheated); pDifficultyLabel->SetText(mission_difficulty); } diff --git a/sdk_src/game/client/swarm/vgui/nb_commander_list_entry.cpp b/sdk_src/game/client/swarm/vgui/nb_commander_list_entry.cpp index dae963a..0d84e15 100644 --- a/sdk_src/game/client/swarm/vgui/nb_commander_list_entry.cpp +++ b/sdk_src/game/client/swarm/vgui/nb_commander_list_entry.cpp @@ -87,7 +87,7 @@ void CNB_Commander_List_Entry::OnThink() m_pCommanderName->SetText( pPlayer->GetPlayerName() ); int nXP = pPlayer->GetExperienceBeforeDebrief() + pPlayer->GetEarnedXP( ASW_XP_TOTAL ); - int nLevel = LevelFromXP( nXP ); + int nLevel = LevelFromXP( nXP, pPlayer->GetPromotion() ); wchar_t szLevelNum[16]=L""; _snwprintf( szLevelNum, ARRAYSIZE( szLevelNum ), L"%i", nLevel + 1 ); // levels start at 0 in code, but show from 1 in the UI diff --git a/sdk_src/game/client/swarm/vgui/nb_lobby_row.cpp b/sdk_src/game/client/swarm/vgui/nb_lobby_row.cpp index 25ccf07..feaf20a 100644 --- a/sdk_src/game/client/swarm/vgui/nb_lobby_row.cpp +++ b/sdk_src/game/client/swarm/vgui/nb_lobby_row.cpp @@ -77,10 +77,11 @@ CNB_Lobby_Row::CNB_Lobby_Row( vgui::Panel *parent, const char *name ) : BaseClas m_pXPBar->SetShowMaxOnCounter( true ); m_pXPBar->SetColors( Color( 255, 255, 255, 0 ), Color( 93,148,192,255 ), Color( 255, 255, 255, 255 ), Color( 17,37,57,255 ), Color( 35, 77, 111, 255 ) ); //m_pXPBar->m_bShowCumulativeTotal = true; - m_pXPBar->AddMinMax( 0, g_iLevelExperience[ 0 ] ); + m_nLastPromotion = 0; + m_pXPBar->AddMinMax( 0, g_iLevelExperience[ 0 ] * g_flPromotionXPScale[ m_nLastPromotion ] ); for ( int i = 0; i < ASW_NUM_EXPERIENCE_LEVELS - 1; i++ ) { - m_pXPBar->AddMinMax( g_iLevelExperience[ i ], g_iLevelExperience[ i + 1 ] ); + m_pXPBar->AddMinMax( g_iLevelExperience[ i ] * g_flPromotionXPScale[ m_nLastPromotion ], g_iLevelExperience[ i + 1 ] * g_flPromotionXPScale[ m_nLastPromotion ] ); } m_pXPBar->m_flBorder = 1.5f; diff --git a/sdk_src/game/client/swarm/vgui/nb_lobby_row.h b/sdk_src/game/client/swarm/vgui/nb_lobby_row.h index 1ebe8f9..3b7ce6f 100644 --- a/sdk_src/game/client/swarm/vgui/nb_lobby_row.h +++ b/sdk_src/game/client/swarm/vgui/nb_lobby_row.h @@ -68,6 +68,7 @@ public: char m_szLastWeaponImage[ ASW_NUM_INVENTORY_SLOTS ][ 255 ]; char m_szLastPortraitImage[ 255 ]; CSteamID m_lastSteamID; + int m_nLastPromotion; int m_nLobbySlot; }; diff --git a/sdk_src/game/client/swarm/vgui/nb_lobby_tooltip.cpp b/sdk_src/game/client/swarm/vgui/nb_lobby_tooltip.cpp index 2457296..1fc7c24 100644 --- a/sdk_src/game/client/swarm/vgui/nb_lobby_tooltip.cpp +++ b/sdk_src/game/client/swarm/vgui/nb_lobby_tooltip.cpp @@ -170,6 +170,9 @@ void CNB_Lobby_Tooltip::OnTick() case 1: m_pPromotionLabel->SetText( "#nb_first_promotion" ); m_pTitle->SetText( "#nb_promotion_medal_1"); break; case 2: m_pPromotionLabel->SetText( "#nb_second_promotion" ); m_pTitle->SetText( "#nb_promotion_medal_2"); break; case 3: m_pPromotionLabel->SetText( "#nb_third_promotion" ); m_pTitle->SetText( "#nb_promotion_medal_3"); break; + case 4: m_pPromotionLabel->SetText( "#nb_fourth_promotion" ); m_pTitle->SetText( "#nb_promotion_medal_4"); break; + case 5: m_pPromotionLabel->SetText( "#nb_fifth_promotion" ); m_pTitle->SetText( "#nb_promotion_medal_5"); break; + case 6: m_pPromotionLabel->SetText( "#nb_sixth_promotion" ); m_pTitle->SetText( "#nb_promotion_medal_6"); break; } m_pPromotionIcon->SetImage( VarArgs( "briefing/promotion_%d_LG", nPromotion ) ); } diff --git a/sdk_src/game/client/swarm/vgui/nb_main_panel.cpp b/sdk_src/game/client/swarm/vgui/nb_main_panel.cpp index e35314c..0a5c6aa 100644 --- a/sdk_src/game/client/swarm/vgui/nb_main_panel.cpp +++ b/sdk_src/game/client/swarm/vgui/nb_main_panel.cpp @@ -158,7 +158,7 @@ void CNB_Main_Panel::OnThink() m_pChatButton->SetVisible( gpGlobals->maxClients > 1 ); m_pVoteButton->SetVisible( gpGlobals->maxClients > 1 ); C_ASW_Player *pPlayer = C_ASW_Player::GetLocalASWPlayer(); - m_pPromotionButton->SetVisible( pPlayer && pPlayer->GetExperience() >= ASW_XP_CAP && pPlayer->GetPromotion() < ASW_PROMOTION_CAP ); + m_pPromotionButton->SetVisible( pPlayer && pPlayer->GetExperience() >= ( ASW_XP_CAP * g_flPromotionXPScale[ pPlayer->GetPromotion() ] ) && pPlayer->GetPromotion() < ASW_PROMOTION_CAP ); diff --git a/sdk_src/game/client/swarm/vgui/nb_mission_options.cpp b/sdk_src/game/client/swarm/vgui/nb_mission_options.cpp index 596f7bf..2ccf420 100644 --- a/sdk_src/game/client/swarm/vgui/nb_mission_options.cpp +++ b/sdk_src/game/client/swarm/vgui/nb_mission_options.cpp @@ -70,6 +70,8 @@ void CNB_Mission_Options::OnThink() m_pSkillLevelLabel->SetText("#asw_difficulty_hard"); else if (m_iLastSkillLevel == 1) m_pSkillLevelLabel->SetText("#asw_difficulty_easy"); + else if (m_iLastSkillLevel == 5) + m_pSkillLevelLabel->SetText("#asw_difficulty_imba"); else m_pSkillLevelLabel->SetText("#asw_difficulty_normal"); @@ -80,6 +82,7 @@ void CNB_Mission_Options::OnThink() case 2: m_pSkillDescriptionLabel->SetText("#asw_difficulty_chooser_normald"); break; case 3: m_pSkillDescriptionLabel->SetText("#asw_difficulty_chooser_hardd"); break; case 4: m_pSkillDescriptionLabel->SetText("#asw_difficulty_chooser_insaned"); break; + case 5: m_pSkillDescriptionLabel->SetText("#asw_difficulty_chooser_imbad"); break; default: m_pSkillDescriptionLabel->SetText("???"); break; } diff --git a/sdk_src/game/client/swarm/vgui/nb_mission_panel.cpp b/sdk_src/game/client/swarm/vgui/nb_mission_panel.cpp index 44da071..2aa69ee 100644 --- a/sdk_src/game/client/swarm/vgui/nb_mission_panel.cpp +++ b/sdk_src/game/client/swarm/vgui/nb_mission_panel.cpp @@ -19,6 +19,8 @@ #include "c_asw_game_resource.h" #include "asw_input.h" #include "nb_island.h" +#include "gameui/swarm/basemodpanel.h" +#include "gameui/swarm/VFooterPanel.h" using namespace vgui; @@ -45,6 +47,8 @@ CNB_Mission_Panel::CNB_Mission_Panel( vgui::Panel *parent, const char *name ) : // == MANAGED_MEMBER_CREATION_END == m_pBackButton = new CNB_Button( this, "BackButton", "", this, "BackButton" ); m_drpDifficulty = new BaseModUI::DropDownMenu( this, "DrpDifficulty" ); + m_drpFriendlyFire = new BaseModUI::DropDownMenu( this, "DrpFriendlyFire" ); + m_drpOnslaught = new BaseModUI::DropDownMenu( this, "DrpOnslaught" ); m_drpFixedSkillPoints = new BaseModUI::DropDownMenu( this, "DrpFixedSkillPoints" ); m_pHeaderFooter->SetTitle( "#nb_mission_details" ); @@ -61,6 +65,8 @@ CNB_Mission_Panel::CNB_Mission_Panel( vgui::Panel *parent, const char *name ) : m_iLastSkillLevel = -1; m_iLastFixedSkillPoints = -1; + m_iLastHardcoreFF = -1; + m_iLastOnslaught = -1; } CNB_Mission_Panel::~CNB_Mission_Panel() @@ -165,6 +171,8 @@ void CNB_Mission_Panel::OnThink() bool bLeader = ( pPlayer && (pPlayer->entindex() == iLeaderIndex ) ); m_drpDifficulty->SetEnabled( ASWGameRules()->GetGameState() == ASW_GS_BRIEFING && bLeader ); + m_drpFriendlyFire->SetEnabled( ASWGameRules()->GetGameState() == ASW_GS_BRIEFING && bLeader ); + m_drpOnslaught->SetEnabled( ASWGameRules()->GetGameState() == ASW_GS_BRIEFING && bLeader ); m_drpFixedSkillPoints->SetEnabled( false ); //ASWGameRules()->GetGameState() == ASW_GS_BRIEFING && bLeader ); if (m_iLastSkillLevel != ASWGameRules()->GetSkillLevel()) @@ -173,27 +181,69 @@ void CNB_Mission_Panel::OnThink() if (m_iLastSkillLevel == 4) { m_drpDifficulty->SetCurrentSelection("#L4D360UI_Difficulty_insane"); - m_pDifficultyDescription->SetText( "#asw_difficulty_chooser_insaned" ); + //m_pDifficultyDescription->SetText( "#asw_difficulty_chooser_insaned" ); } else if (m_iLastSkillLevel == 3) { m_drpDifficulty->SetCurrentSelection("#L4D360UI_Difficulty_hard"); - m_pDifficultyDescription->SetText( "#asw_difficulty_chooser_hardd" ); + //m_pDifficultyDescription->SetText( "#asw_difficulty_chooser_hardd" ); } else if (m_iLastSkillLevel == 1) { m_drpDifficulty->SetCurrentSelection("#L4D360UI_Difficulty_easy"); - m_pDifficultyDescription->SetText( "#asw_difficulty_chooser_easyd" ); + //m_pDifficultyDescription->SetText( "#asw_difficulty_chooser_easyd" ); + } + else if (m_iLastSkillLevel == 5) + { + m_drpDifficulty->SetCurrentSelection("#L4D360UI_Difficulty_imba"); + //m_pDifficultyDescription->SetText( "#asw_difficulty_chooser_imbad" ); } else { m_drpDifficulty->SetCurrentSelection("#L4D360UI_Difficulty_normal"); - m_pDifficultyDescription->SetText( "#asw_difficulty_chooser_normald" ); + //m_pDifficultyDescription->SetText( "#asw_difficulty_chooser_normald" ); } } + extern ConVar asw_sentry_friendly_fire_scale; + extern ConVar asw_marine_ff_absorption; + int nHardcoreFF = ( asw_sentry_friendly_fire_scale.GetFloat() != 0.0f || asw_marine_ff_absorption.GetInt() != 1 ) ? 1 : 0; + if ( m_iLastHardcoreFF != nHardcoreFF ) + { + m_iLastHardcoreFF = nHardcoreFF; + if ( nHardcoreFF == 1 ) + { + m_drpFriendlyFire->SetCurrentSelection( "#L4D360UI_HardcoreFF" ); + } + else + { + m_drpFriendlyFire->SetCurrentSelection( "#L4D360UI_RegularFF" ); + } + } + extern ConVar asw_horde_override; + extern ConVar asw_wanderer_override; + int nOnslaught = ( asw_horde_override.GetBool() || asw_wanderer_override.GetBool() ) ? 1 : 0; + if ( m_iLastOnslaught != nOnslaught ) + { + m_iLastOnslaught = nOnslaught; + if ( nOnslaught == 1 ) + { + m_drpOnslaught->SetCurrentSelection( "#L4D360UI_OnslaughtEnabled" ); + } + else + { + m_drpOnslaught->SetCurrentSelection( "#L4D360UI_OnslaughtDisabled" ); + } + } + + BaseModUI::CBaseModFooterPanel *footer = BaseModUI::CBaseModPanel::GetSingleton().GetFooterPanel(); + if ( footer ) + { + m_pDifficultyDescription->SetText( footer->GetHelpText() ); + } // only show insane in multiplayer m_drpDifficulty->SetFlyoutItemEnabled( "BtnImpossible", gpGlobals->maxClients > 1 ); + m_drpDifficulty->SetFlyoutItemEnabled( "BtnImba", gpGlobals->maxClients > 1 ); if ( ASWGameRules()->IsCampaignGame() && ASWGameRules()->GetCampaignSave() && ASWGameRules()->GetGameState() != ASW_GS_INGAME ) { @@ -265,6 +315,31 @@ void CNB_Mission_Panel::OnCommand( const char *command ) engine->ClientCmd( "cl_skill 4" ); return; } + else if ( !Q_stricmp( command, "#L4D360UI_Difficulty_imba" ) ) + { + engine->ClientCmd( "cl_skill 5" ); + return; + } + else if ( !Q_stricmp( command, "#L4D360UI_RegularFF" ) ) + { + engine->ClientCmd( "cl_hardcore_ff 0" ); + return; + } + else if ( !Q_stricmp( command, "#L4D360UI_HardcoreFF" ) ) + { + engine->ClientCmd( "cl_hardcore_ff 1" ); + return; + } + else if ( !Q_stricmp( command, "#L4D360UI_OnslaughtDisabled" ) ) + { + engine->ClientCmd( "cl_onslaught 0" ); + return; + } + else if ( !Q_stricmp( command, "#L4D360UI_OnslaughtEnabled" ) ) + { + engine->ClientCmd( "cl_onslaught 1" ); + return; + } BaseClass::OnCommand( command ); } diff --git a/sdk_src/game/client/swarm/vgui/nb_mission_panel.h b/sdk_src/game/client/swarm/vgui/nb_mission_panel.h index 985a584..e8e0b5e 100644 --- a/sdk_src/game/client/swarm/vgui/nb_mission_panel.h +++ b/sdk_src/game/client/swarm/vgui/nb_mission_panel.h @@ -45,6 +45,8 @@ public: CNB_Button *m_pBackButton; BaseModUI::DropDownMenu* m_drpDifficulty; BaseModUI::DropDownMenu* m_drpFixedSkillPoints; + BaseModUI::DropDownMenu* m_drpFriendlyFire; + BaseModUI::DropDownMenu* m_drpOnslaught; ObjectiveListBox* m_pObjectiveList; ObjectiveDetailsPanel* m_pObjectiveDetails; @@ -59,6 +61,8 @@ public: int m_iLastSkillLevel; int m_iLastFixedSkillPoints; + int m_iLastHardcoreFF; + int m_iLastOnslaught; }; class InGameMissionPanelFrame : public vgui::Frame diff --git a/sdk_src/game/client/swarm/vgui/nb_mission_summary.cpp b/sdk_src/game/client/swarm/vgui/nb_mission_summary.cpp index 4d5f147..717f311 100644 --- a/sdk_src/game/client/swarm/vgui/nb_mission_summary.cpp +++ b/sdk_src/game/client/swarm/vgui/nb_mission_summary.cpp @@ -105,14 +105,31 @@ void CNB_Mission_Summary::OnThink() return; int nSkillLevel = ASWGameRules()->GetSkillLevel(); - if (nSkillLevel == 4) - m_pDifficultyLabel->SetText("#asw_difficulty_insane"); - else if (nSkillLevel == 3) - m_pDifficultyLabel->SetText("#asw_difficulty_hard"); - else if (nSkillLevel == 1) - m_pDifficultyLabel->SetText("#asw_difficulty_easy"); - else - m_pDifficultyLabel->SetText("#asw_difficulty_normal"); + const wchar_t *pDifficulty = NULL; + switch( nSkillLevel ) + { + case 1: pDifficulty = g_pVGuiLocalize->Find( "#asw_difficulty_easy" ); break; + default: + case 2: pDifficulty = g_pVGuiLocalize->Find( "#asw_difficulty_normal" ); break; + case 3: pDifficulty = g_pVGuiLocalize->Find( "#asw_difficulty_hard" ); break; + case 4: pDifficulty = g_pVGuiLocalize->Find( "#asw_difficulty_insane" ); break; + case 5: pDifficulty = g_pVGuiLocalize->Find( "#asw_difficulty_imba" ); break; + } + if ( !pDifficulty ) + { + pDifficulty = L""; + } + + if ( CAlienSwarm::IsOnslaught() ) + { + wchar_t wszText[ 128 ]; + _snwprintf( wszText, sizeof( wszText ), L"%s %s", pDifficulty, g_pVGuiLocalize->FindSafe( "#nb_onslaught_title" ) ); + m_pDifficultyLabel->SetText( wszText ); + } + else + { + m_pDifficultyLabel->SetText( pDifficulty ); + } CASWHudMinimap *pMap = GET_HUDELEMENT( CASWHudMinimap ); if ( pMap ) diff --git a/sdk_src/game/server/ai_basenpc.cpp b/sdk_src/game/server/ai_basenpc.cpp index 1fc945d..c49ea00 100644 --- a/sdk_src/game/server/ai_basenpc.cpp +++ b/sdk_src/game/server/ai_basenpc.cpp @@ -642,8 +642,8 @@ void CAI_BaseNPC::CleanupOnDeath( CBaseEntity *pCulprit, bool bFireDeathOutput ) RemoveActorFromScriptedScenes( this, false /*all scenes*/ ); } - else - DevMsg( "Unexpected double-death-cleanup\n" ); + //else + //DevMsg( "Unexpected double-death-cleanup\n" ); } void CAI_BaseNPC::SelectDeathPose( const CTakeDamageInfo &info ) diff --git a/sdk_src/game/server/gameinterface.cpp b/sdk_src/game/server/gameinterface.cpp index 70367a0..da6ea56 100644 --- a/sdk_src/game/server/gameinterface.cpp +++ b/sdk_src/game/server/gameinterface.cpp @@ -1900,13 +1900,15 @@ void CServerGameDLL::GetMatchmakingTags( char *buf, size_t bufSize ) #ifdef INFESTED_DLL extern ConVar asw_marine_ff_absorption; extern ConVar asw_sentry_friendly_fire_scale; + extern ConVar asw_horde_override; + extern ConVar asw_wanderer_override; extern ConVar asw_skill; char * const bufBase = buf; int len = 0; // hardcore friendly fire - if ( asw_marine_ff_absorption.GetInt() != 1 || asw_sentry_friendly_fire_scale.GetFloat() != 0.0f ) + if ( CAlienSwarm::IsHardcoreFF() ) { Q_strncpy( buf, "HardcoreFF,", bufSize ); len = strlen( buf ); @@ -1914,6 +1916,15 @@ void CServerGameDLL::GetMatchmakingTags( char *buf, size_t bufSize ) bufSize -= len; } + // onslaught + if ( CAlienSwarm::IsOnslaught() ) + { + Q_strncpy( buf, "Onslaught,", bufSize ); + len = strlen( buf ); + buf += len; + bufSize -= len; + } + // difficulty level const char *szSkill = "Normal,"; switch( asw_skill.GetInt() ) @@ -1921,6 +1932,7 @@ void CServerGameDLL::GetMatchmakingTags( char *buf, size_t bufSize ) case 1: szSkill = "Easy,"; break; case 3: szSkill = "Hard,"; break; case 4: szSkill = "Insane,"; break; + case 5: szSkill = "Imba,"; break; } Q_strncpy( buf, szSkill, bufSize ); len = strlen( buf ); diff --git a/sdk_src/game/server/swarm/asw_alien.cpp b/sdk_src/game/server/swarm/asw_alien.cpp index 1c6b0f3..3b59867 100644 --- a/sdk_src/game/server/swarm/asw_alien.cpp +++ b/sdk_src/game/server/swarm/asw_alien.cpp @@ -29,6 +29,7 @@ #include "datacache/imdlcache.h" #include "asw_tesla_trap.h" #include "sendprop_priorities.h" +#include "asw_spawn_manager.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -151,6 +152,7 @@ IMPLEMENT_AUTO_LIST( IAlienAutoList ); CASW_Alien::CASW_Alien( void ) : m_BehaviorParms( DefLessFunc( const CUtlSymbol ) ) { + m_bRegisteredAsAwake = false; m_pszAlienModelName = NULL; m_bRunAtChasingPathEnds = true; m_bPerformingZigZag = false; @@ -493,6 +495,32 @@ void CASW_Alien::UpdateSleepState(bool bInPVS) } } } + if ( GetSleepState() == AISS_AWAKE ) + { + if ( !m_bRegisteredAsAwake ) + { + ASWSpawnManager()->OnAlienWokeUp( this ); + m_bRegisteredAsAwake = true; + } + } + else + { + if ( m_bRegisteredAsAwake ) + { + ASWSpawnManager()->OnAlienSleeping( this ); + m_bRegisteredAsAwake = false; + } + } +} + +void CASW_Alien::UpdateOnRemove() +{ + if ( m_bRegisteredAsAwake ) + { + m_bRegisteredAsAwake = false; + ASWSpawnManager()->OnAlienSleeping( this ); + } + BaseClass::UpdateOnRemove(); } void CASW_Alien::SetDistSwarmSense( float flDistSense ) diff --git a/sdk_src/game/server/swarm/asw_alien.h b/sdk_src/game/server/swarm/asw_alien.h index 2176bd2..10963c5 100644 --- a/sdk_src/game/server/swarm/asw_alien.h +++ b/sdk_src/game/server/swarm/asw_alien.h @@ -93,6 +93,8 @@ public: // make the aliens wake up when a marine gets within a certain distance void UpdateSleepState( bool bInPVS ); void UpdateEfficiency( bool bInPVS ); + virtual void UpdateOnRemove(); + bool m_bRegisteredAsAwake; float m_fLastSleepCheckTime; bool m_bVisibleWhenAsleep; diff --git a/sdk_src/game/server/swarm/asw_base_spawner.cpp b/sdk_src/game/server/swarm/asw_base_spawner.cpp index e34b18b..14db72e 100644 --- a/sdk_src/game/server/swarm/asw_base_spawner.cpp +++ b/sdk_src/game/server/swarm/asw_base_spawner.cpp @@ -316,10 +316,13 @@ CBaseEntity* CASW_Base_Spawner::GetAlienOrderTarget() bool CASW_Base_Spawner::IsValidOnThisSkillLevel() { - if (m_iMinSkillLevel > 0 && ASWGameRules()->GetSkillLevel() < m_iMinSkillLevel) + // treat difficulty 5 and 4 as the same + int nSkillLevel = ASWGameRules()->GetSkillLevel(); + nSkillLevel = clamp( nSkillLevel, 1, 4 ); + if (m_iMinSkillLevel > 0 && nSkillLevel < m_iMinSkillLevel) return false; if (m_iMaxSkillLevel > 0 && m_iMaxSkillLevel < 10 - && ASWGameRules()->GetSkillLevel() > m_iMaxSkillLevel) + && nSkillLevel > m_iMaxSkillLevel) return false; return true; } diff --git a/sdk_src/game/server/swarm/asw_buzzer.cpp b/sdk_src/game/server/swarm/asw_buzzer.cpp index 6adc922..489f1fa 100644 --- a/sdk_src/game/server/swarm/asw_buzzer.cpp +++ b/sdk_src/game/server/swarm/asw_buzzer.cpp @@ -2745,7 +2745,9 @@ float CASW_Buzzer::GetMaxEnginePower() if (ASWGameRules()) { - return ASWGameRules()->GetSkillLevel() + 0.1f; + int nSkillLevel = ASWGameRules()->GetSkillLevel(); + nSkillLevel = clamp( nSkillLevel, 1, 4 ); + return nSkillLevel + 0.1f; } return 2.0f; } diff --git a/sdk_src/game/server/swarm/asw_director.cpp b/sdk_src/game/server/swarm/asw_director.cpp index bb4dad5..5b76677 100644 --- a/sdk_src/game/server/swarm/asw_director.cpp +++ b/sdk_src/game/server/swarm/asw_director.cpp @@ -22,12 +22,12 @@ ConVar asw_director_debug("asw_director_debug", "0", FCVAR_CHEAT, "Displays dire extern ConVar asw_intensity_far_range; extern ConVar asw_spawning_enabled; -ConVar asw_horde_override( "asw_horde_override", "0", FCVAR_NONE, "Forces hordes to spawn" ); -ConVar asw_wanderer_override( "asw_wanderer_override", "0", FCVAR_NONE, "Forces wanderers to spawn" ); -ConVar asw_horde_interval_min("asw_horde_interval_min", "40", FCVAR_CHEAT, "Min time between hordes" ); -ConVar asw_horde_interval_max("asw_horde_interval_max", "60", FCVAR_CHEAT, "Min time between hordes" ); +extern ConVar asw_horde_override; +extern ConVar asw_wanderer_override; +ConVar asw_horde_interval_min("asw_horde_interval_min", "45", FCVAR_CHEAT, "Min time between hordes" ); +ConVar asw_horde_interval_max("asw_horde_interval_max", "65", FCVAR_CHEAT, "Min time between hordes" ); ConVar asw_horde_size_min("asw_horde_size_min", "9", FCVAR_CHEAT, "Min horde size" ); -ConVar asw_horde_size_max("asw_horde_size_max", "12", FCVAR_CHEAT, "Max horde size" ); +ConVar asw_horde_size_max("asw_horde_size_max", "14", FCVAR_CHEAT, "Max horde size" ); ConVar asw_director_relaxed_min_time("asw_director_relaxed_min_time", "25", FCVAR_CHEAT, "Min time that director stops spawning aliens"); ConVar asw_director_relaxed_max_time("asw_director_relaxed_max_time", "40", FCVAR_CHEAT, "Max time that director stops spawning aliens"); @@ -102,6 +102,11 @@ void CASW_Director::LevelInitPreEntity() void CASW_Director::LevelInitPostEntity() { Init(); + + if ( ASWSpawnManager() ) + { + ASWSpawnManager()->LevelInitPostEntity(); + } } void CASW_Director::FrameUpdatePreEntityThink() @@ -250,60 +255,76 @@ void CASW_Director::MarineTookDamage( CASW_Marine *pMarine, const CTakeDamageInf void CASW_Director::UpdateHorde() { - bool bHordesEnabled = m_bHordesEnabled || asw_horde_override.GetBool(); - if ( !bHordesEnabled || !ASWSpawnManager() ) - return; - if ( asw_director_debug.GetInt() > 0 ) { if ( m_bHordeInProgress ) { engine->Con_NPrintf( 11, "Horde in progress. Left to spawn = %d", ASWSpawnManager()->GetHordeToSpawn() ); } - else - { - engine->Con_NPrintf( 11, "Next Horde due: %f", m_HordeTimer.GetRemainingTime() ); - } + engine->Con_NPrintf( 12, "Next Horde due: %f", m_HordeTimer.GetRemainingTime() ); + + engine->Con_NPrintf( 15, "Awake aliens: %d\n", ASWSpawnManager()->GetAwakeAliens() ); + engine->Con_NPrintf( 16, "Awake drones: %d\n", ASWSpawnManager()->GetAwakeDrones() ); } - if ( !m_bHordeInProgress ) + bool bHordesEnabled = m_bHordesEnabled || asw_horde_override.GetBool(); + if ( !bHordesEnabled || !ASWSpawnManager() ) + return; + + if ( !m_HordeTimer.HasStarted() ) { - if ( !m_HordeTimer.HasStarted() ) + float flDuration = RandomFloat( asw_horde_interval_min.GetFloat(), asw_horde_interval_max.GetFloat() ); + if ( m_bFinale ) { - float flDuration = RandomFloat( asw_horde_interval_min.GetFloat(), asw_horde_interval_max.GetFloat() ); - if ( m_bFinale ) - { - flDuration = RandomFloat( 5.0f, 10.0f ); - } - Msg( "Will be spawning a horde in %f seconds\n", flDuration ); - m_HordeTimer.Start( flDuration ); + flDuration = RandomFloat( 5.0f, 10.0f ); } - else if ( m_HordeTimer.IsElapsed() ) + if ( asw_director_debug.GetBool() ) + { + Msg( "Will be spawning a horde in %f seconds\n", flDuration ); + } + m_HordeTimer.Start( flDuration ); + } + else if ( m_HordeTimer.IsElapsed() ) + { + if ( ASWSpawnManager()->GetAwakeDrones() < 25 ) { int iNumAliens = RandomInt( asw_horde_size_min.GetInt(), asw_horde_size_max.GetInt() ); + if ( ASWSpawnManager()->AddHorde( iNumAliens ) ) { - Msg("Created horde of size %d\n", iNumAliens); + if ( asw_director_debug.GetBool() ) + { + Msg("Created horde of size %d\n", iNumAliens); + } m_bHordeInProgress = true; if ( ASWGameRules() ) { ASWGameRules()->BroadcastSound( "Spawner.Horde" ); } + m_HordeTimer.Invalidate(); } else { - m_HordeTimer.Invalidate(); + // if we failed to find a horde position, try again shortly. + m_HordeTimer.Start( RandomFloat( 10.0f, 16.0f ) ); } } + else + { + // if there are currently too many awake aliens, then wait 10 seconds before trying again + m_HordeTimer.Start( 10.0f ); + } } } void CASW_Director::OnHordeFinishedSpawning() { - Msg("Horde finishes spawning\n"); + if ( asw_director_debug.GetBool() ) + { + Msg("Horde finishes spawning\n"); + } m_bHordeInProgress = false; - m_HordeTimer.Invalidate(); } void CASW_Director::UpdateSpawningState() @@ -418,7 +439,10 @@ void CASW_Director::UpdateWanderers() if ( ASWSpawnManager() ) { - ASWSpawnManager()->AddAlien(); + if ( ASWSpawnManager()->GetAwakeDrones() < 20 ) + { + ASWSpawnManager()->AddAlien(); + } } } } @@ -496,4 +520,36 @@ void CASW_Director::UpdateMarineInsideEscapeRoom( CASW_Marine *pMarine ) } m_bFiredEscapeRoom = true; +} + +void CASW_Director::OnMissionStarted() +{ + // if we have wanders turned on, spawn a couple of encounters + if ( asw_wanderer_override.GetBool() && ASWGameRules() ) + { + ASWSpawnManager()->SpawnRandomShieldbug(); + + int nParasites = 1; + switch( ASWGameRules()->GetSkillLevel() ) + { + case 1: nParasites = RandomInt( 4, 6 ); break; + default: + case 2: nParasites = RandomInt( 4, 6 ); break; + case 3: nParasites = RandomInt( 5, 7 ); break; + case 4: nParasites = RandomInt( 5, 9 ); break; + case 5: nParasites = RandomInt( 5, 10 ); break; + } + while ( nParasites > 0 ) + { + int nParasitesInThisPack = RandomInt( 3, 6 ); + if ( ASWSpawnManager()->SpawnRandomParasitePack( nParasitesInThisPack ) ) + { + nParasites -= nParasitesInThisPack; + } + else + { + break; + } + } + } } \ No newline at end of file diff --git a/sdk_src/game/server/swarm/asw_director.h b/sdk_src/game/server/swarm/asw_director.h index 7e43d89..a547bca 100644 --- a/sdk_src/game/server/swarm/asw_director.h +++ b/sdk_src/game/server/swarm/asw_director.h @@ -68,6 +68,8 @@ public: void OnMarineStartedHack( CASW_Marine *pMarine, CBaseEntity *pComputer ); void UpdateMarineInsideEscapeRoom( CASW_Marine *pMarine ); + void OnMissionStarted(); + // Spawning hordes of aliens void OnHordeFinishedSpawning(); diff --git a/sdk_src/game/server/swarm/asw_drone_advanced.cpp b/sdk_src/game/server/swarm/asw_drone_advanced.cpp index 5e6b6ba..553ab34 100644 --- a/sdk_src/game/server/swarm/asw_drone_advanced.cpp +++ b/sdk_src/game/server/swarm/asw_drone_advanced.cpp @@ -306,6 +306,7 @@ float CASW_Drone_Advanced::GetIdealSpeed() const switch (ASWGameRules()->GetSkillLevel()) { + case 5: boost *= asw_alien_speed_scale_insane.GetFloat(); break; case 4: boost *= asw_alien_speed_scale_insane.GetFloat(); break; case 3: boost *= asw_alien_speed_scale_hard.GetFloat(); break; case 2: boost *= asw_alien_speed_scale_normal.GetFloat(); break; diff --git a/sdk_src/game/server/swarm/asw_egg.cpp b/sdk_src/game/server/swarm/asw_egg.cpp index 1ebfbab..4eeee2a 100644 --- a/sdk_src/game/server/swarm/asw_egg.cpp +++ b/sdk_src/game/server/swarm/asw_egg.cpp @@ -297,7 +297,7 @@ void CASW_Egg::AnimThink( void ) } else { - if (ASWGameRules() && ASWGameRules()->GetSkillLevel() == 4 ) + if (ASWGameRules() && ASWGameRules()->GetSkillLevel() >= 4 ) { m_fNextMarineCheckTime = gpGlobals->curtime + 1.5f; } diff --git a/sdk_src/game/server/swarm/asw_gameinterface.cpp b/sdk_src/game/server/swarm/asw_gameinterface.cpp index 1cb4d2b..0ae3f40 100644 --- a/sdk_src/game/server/swarm/asw_gameinterface.cpp +++ b/sdk_src/game/server/swarm/asw_gameinterface.cpp @@ -32,6 +32,9 @@ void CServerGameClients::GetPlayerLimits( int& minplayers, int& maxplayers, int void CServerGameDLL::LevelInit_ParseAllEntities( const char *pMapEntities ) { + // precache even if not in the level, for onslaught mode + UTIL_PrecacheOther( "asw_shieldbug" ); + UTIL_PrecacheOther( "asw_parasite" ); } bool g_bOfflineGame = false; @@ -122,6 +125,38 @@ void CServerGameDLL::ApplyGameSettings( KeyValues *pKV ) { asw_skill.SetValue( 4 ); } + else if ( !Q_stricmp( szDifficulty, "imba" ) ) + { + asw_skill.SetValue( 5 ); + } + + extern ConVar asw_sentry_friendly_fire_scale; + extern ConVar asw_marine_ff_absorption; + int nHardcoreFF = pKV->GetInt( "game/hardcoreFF", 0 ); + if ( nHardcoreFF == 1 ) + { + asw_sentry_friendly_fire_scale.SetValue( 1.0f ); + asw_marine_ff_absorption.SetValue( 0 ); + } + else + { + asw_sentry_friendly_fire_scale.SetValue( 0.0f ); + asw_marine_ff_absorption.SetValue( 1 ); + } + + extern ConVar asw_horde_override; + extern ConVar asw_wanderer_override; + int nOnslaught = pKV->GetInt( "game/onslaught", 0 ); + if ( nOnslaught == 1 ) + { + asw_horde_override.SetValue( 1 ); + asw_wanderer_override.SetValue( 1 ); + } + else + { + asw_horde_override.SetValue( 0 ); + asw_wanderer_override.SetValue( 0 ); + } char const *szMapCommand = pKV->GetString( "map/mapcommand", "map" ); diff --git a/sdk_src/game/server/swarm/asw_hack_computer.cpp b/sdk_src/game/server/swarm/asw_hack_computer.cpp index cecdf0f..6d53c35 100644 --- a/sdk_src/game/server/swarm/asw_hack_computer.cpp +++ b/sdk_src/game/server/swarm/asw_hack_computer.cpp @@ -177,7 +177,7 @@ void CASW_Hack_Computer::SelectHackOption(int i) diff_factor = 1.55f; else if (iSkill == 3) diff_factor = 1.50f; - else if (iSkill == 4) + else if (iSkill >= 4) diff_factor = 1.45f; // estimate the time for a fast hack // try, time taken for each column to rotate through twice diff --git a/sdk_src/game/server/swarm/asw_map_scores.cpp b/sdk_src/game/server/swarm/asw_map_scores.cpp index 037c4c7..a4cfb01 100644 --- a/sdk_src/game/server/swarm/asw_map_scores.cpp +++ b/sdk_src/game/server/swarm/asw_map_scores.cpp @@ -144,6 +144,7 @@ const char* CASW_Map_Scores::GetBestKillsKeyName(int iSkill) case 1: return "BestKillsEasy"; break; case 3: return "BestKillsHard"; break; case 4: return "BestKillsInsane"; break; + case 5: return "BestKillsImba"; break; default: break; } return "BestKillsNormal"; @@ -156,6 +157,7 @@ const char* CASW_Map_Scores::GetBestTimeKeyName(int iSkill) case 1: return "BestTimeEasy"; break; case 3: return "BestTimeHard"; break; case 4: return "BestTimeInsane"; break; + case 5: return "BestTimeImba"; break; default: break; } return "BestTimeNormal"; diff --git a/sdk_src/game/server/swarm/asw_marine.cpp b/sdk_src/game/server/swarm/asw_marine.cpp index 67354c1..f775159 100644 --- a/sdk_src/game/server/swarm/asw_marine.cpp +++ b/sdk_src/game/server/swarm/asw_marine.cpp @@ -345,13 +345,6 @@ BEGIN_DATADESC( CASW_Marine ) DEFINE_FIELD( m_bPowerupExpires, FIELD_BOOLEAN ), END_DATADESC() -void UpdateMatchmakingTags(); - -static void FriendlyFireCallback( IConVar *pConVar, const char *pOldValue, float flOldValue ) -{ - UpdateMatchmakingTags(); -} - extern ConVar weapon_showproficiency; extern ConVar asw_leadership_radius; extern ConVar asw_buzzer_poison_duration; @@ -381,7 +374,6 @@ ConVar asw_marine_ff("asw_marine_ff", "1", FCVAR_CHEAT, "Marine friendly fire se ConVar asw_marine_ff_guard_time("asw_marine_ff_guard_time", "5.0", FCVAR_CHEAT, "Amount of time firing is disabled for when activating friendly fire guard"); ConVar asw_marine_ff_dmg_base("asw_marine_ff_dmg_base", "1.0", FCVAR_CHEAT, "Amount of friendly fire damage on mission difficulty 5"); ConVar asw_marine_ff_dmg_step("asw_marine_ff_dmg_step", "0.2", FCVAR_CHEAT, "Amount friendly fire damage is modified per mission difficuly level away from 5"); -ConVar asw_marine_ff_absorption("asw_marine_ff_absorption", "1", FCVAR_NONE, "Friendly fire absorption style (0=none 1=ramp up 2=ramp down)", FriendlyFireCallback ); ConVar asw_marine_ff_absorption_decay_rate("asw_marine_ff_absorption_decay_rate", "0.33f", FCVAR_CHEAT, "Rate of FF absorption decay"); ConVar asw_marine_ff_absorption_build_rate("asw_marine_ff_absorption_build_rate", "0.25f", FCVAR_CHEAT, "Rate of FF absorption decay build up when being shot by friendlies"); ConVar asw_marine_burn_time_easy("asw_marine_burn_time_easy", "6", FCVAR_CHEAT, "Amount of time marine burns for when ignited on easy difficulty"); @@ -395,7 +387,8 @@ ConVar asw_marine_special_idle_chatter_chance("asw_marine_special_idle_chatter_c ConVar asw_force_ai_fire("asw_force_ai_fire", "0", FCVAR_CHEAT, "Forces all AI marines to fire constantly"); ConVar asw_realistic_death_chatter("asw_realistic_death_chatter", "0", FCVAR_NONE, "If true, only 1 nearby marine will shout about marine deaths"); ConVar asw_god( "asw_god", "0", FCVAR_CHEAT, "Set to 1 to make marines invulnerable" ); -ConVar asw_sentry_friendly_fire_scale( "asw_sentry_friendly_fire_scale", "0", FCVAR_NONE, "Damage scale for sentry gun friendly fire", FriendlyFireCallback ); +extern ConVar asw_sentry_friendly_fire_scale; +extern ConVar asw_marine_ff_absorption; ConVar asw_movement_direction_tolerance( "asw_movement_direction_tolerance", "30.0", FCVAR_CHEAT ); ConVar asw_movement_direction_interval( "asw_movement_direction_interval", "0.5", FCVAR_CHEAT ); @@ -4058,7 +4051,7 @@ void CASW_Marine::ASW_Ignite( float flFlameLifetime, float flSize, CBaseEntity * flFlameLifetime *= asw_marine_burn_time_normal.GetFloat(); else if (iDiff == 3) flFlameLifetime *= asw_marine_burn_time_hard.GetFloat(); - else if (iDiff == 4) + else if (iDiff == 4 || iDiff == 5) flFlameLifetime *= asw_marine_burn_time_insane.GetFloat(); if ( m_flFirstBurnTime == 0 ) @@ -4127,7 +4120,8 @@ bool CASW_Marine::AllowedToIgnite( void ) if ( m_iJumpJetting.Get() != 0 ) return false; - if ( m_flFirstBurnTime > 0 && (gpGlobals->curtime - m_flFirstBurnTime) >= asw_marine_time_until_ignite.GetFloat() ) + float flBurnTime = ( asw_marine_ff_absorption.GetInt() > 0 ) ? asw_marine_time_until_ignite.GetFloat() : 0.2f; + if ( m_flFirstBurnTime > 0 && (gpGlobals->curtime - m_flFirstBurnTime) >= flBurnTime ) return true; // don't ignite, but play a flesh burn sound if we aren't on fire already diff --git a/sdk_src/game/server/swarm/asw_marine_schedule.cpp b/sdk_src/game/server/swarm/asw_marine_schedule.cpp index d9583a5..a08ba2b 100644 --- a/sdk_src/game/server/swarm/asw_marine_schedule.cpp +++ b/sdk_src/game/server/swarm/asw_marine_schedule.cpp @@ -169,6 +169,7 @@ float CASW_Marine::GetCloseCombatSightRange() case 2: return ASW_CLOSE_COMBAT_SIGHT_RANGE_NORMAL; break; case 3: return ASW_CLOSE_COMBAT_SIGHT_RANGE_HARD; break; case 4: + case 5: default: return ASW_CLOSE_COMBAT_SIGHT_RANGE_INSANE; break; } } diff --git a/sdk_src/game/server/swarm/asw_parasite.cpp b/sdk_src/game/server/swarm/asw_parasite.cpp index f3b8c98..628900f 100644 --- a/sdk_src/game/server/swarm/asw_parasite.cpp +++ b/sdk_src/game/server/swarm/asw_parasite.cpp @@ -1089,6 +1089,7 @@ void CASW_Parasite::UpdatePlaybackRate() float boost = asw_parasite_speedboost.GetFloat(); switch (ASWGameRules()->GetSkillLevel()) { + case 5: boost *= asw_alien_speed_scale_insane.GetFloat(); break; case 4: boost *= asw_alien_speed_scale_insane.GetFloat(); break; case 3: boost *= asw_alien_speed_scale_hard.GetFloat(); break; case 2: boost *= asw_alien_speed_scale_normal.GetFloat(); break; diff --git a/sdk_src/game/server/swarm/asw_player.cpp b/sdk_src/game/server/swarm/asw_player.cpp index 1aa1015..f74ac4d 100644 --- a/sdk_src/game/server/swarm/asw_player.cpp +++ b/sdk_src/game/server/swarm/asw_player.cpp @@ -299,7 +299,7 @@ void ASW_DrawAwakeAI() int iVEfficient = 0; int iSEfficient = 0; int iNormal = 0; - int nprintIndex = 0; + int nprintIndex = 18; engine->Con_NPrintf( nprintIndex, "AI (awake/asleep) (normal/efficient/very efficient/super efficient/dormant)"); nprintIndex++; engine->Con_NPrintf( nprintIndex, "================================"); @@ -789,6 +789,76 @@ bool CASW_Player::ClientCommand( const CCommand &args ) ASWGameRules()->RequestSkillDown(this); return true; } + else if ( FStrEq( pcmd, "cl_hardcore_ff") ) + { + if ( args.ArgC() < 2 ) + { + Warning("Player sent a bad cl_hardcore_ff command\n"); + return false; + } + + if ( ASWGameResource() && ASWGameResource()->GetLeader() == this ) + { + bool bOldHardcoreMode = CAlienSwarm::IsHardcoreFF(); + int nHardcore = atoi( args[1] ); + nHardcore = clamp( nHardcore, 0, 1 ); + + extern ConVar asw_sentry_friendly_fire_scale; + extern ConVar asw_marine_ff_absorption; + asw_sentry_friendly_fire_scale.SetValue( nHardcore ); + asw_marine_ff_absorption.SetValue( 1 - nHardcore ); + + if ( CAlienSwarm::IsHardcoreFF() != bOldHardcoreMode ) + { + CReliableBroadcastRecipientFilter filter; + filter.RemoveRecipient( this ); // notify everyone except the player changing the setting + if ( nHardcore > 0 ) + { + UTIL_ClientPrintFilter( filter, ASW_HUD_PRINTTALKANDCONSOLE, "#asw_enabled_hardcoreff", GetPlayerName() ); + } + else + { + UTIL_ClientPrintFilter( filter, ASW_HUD_PRINTTALKANDCONSOLE, "#asw_disabled_hardcoreff", GetPlayerName() ); + } + } + } + return true; + } + else if ( FStrEq( pcmd, "cl_onslaught") ) + { + if ( args.ArgC() < 2 ) + { + Warning("Player sent a bad cl_onslaught command\n"); + return false; + } + + if ( ASWGameResource() && ASWGameResource()->GetLeader() == this ) + { + bool bOldOnslaughtMode = CAlienSwarm::IsOnslaught(); + int nOnslaught = atoi( args[1] ); + nOnslaught = clamp( nOnslaught, 0, 1 ); + + extern ConVar asw_horde_override; + extern ConVar asw_wanderer_override; + asw_horde_override.SetValue( nOnslaught ); + asw_wanderer_override.SetValue( nOnslaught ); + + if ( CAlienSwarm::IsOnslaught() != bOldOnslaughtMode ) + { + CReliableBroadcastRecipientFilter filter; + filter.RemoveRecipient( this ); // notify everyone except the player changing the setting + if ( nOnslaught > 0 ) + { + UTIL_ClientPrintFilter( filter, ASW_HUD_PRINTTALKANDCONSOLE, "#asw_enabled_onslaught", GetPlayerName() ); + } + else + { + UTIL_ClientPrintFilter( filter, ASW_HUD_PRINTTALKANDCONSOLE, "#asw_disabled_onslaught", GetPlayerName() ); + } + } + } + return true; + } else if ( FStrEq( pcmd, "cl_fixedskills") ) { /* diff --git a/sdk_src/game/server/swarm/asw_queen.cpp b/sdk_src/game/server/swarm/asw_queen.cpp index 997f167..f5cfc03 100644 --- a/sdk_src/game/server/swarm/asw_queen.cpp +++ b/sdk_src/game/server/swarm/asw_queen.cpp @@ -1418,6 +1418,7 @@ void CASW_Queen::SetHealthByDifficultyLevel() case 2: health = asw_queen_health_normal.GetInt(); break; case 3: health = asw_queen_health_hard.GetInt(); break; case 4: health = asw_queen_health_insane.GetInt(); break; + case 5: health = asw_queen_health_insane.GetInt(); break; default: 5000; } } diff --git a/sdk_src/game/server/swarm/asw_spawn_manager.cpp b/sdk_src/game/server/swarm/asw_spawn_manager.cpp index 7dd4325..9a3ca45 100644 --- a/sdk_src/game/server/swarm/asw_spawn_manager.cpp +++ b/sdk_src/game/server/swarm/asw_spawn_manager.cpp @@ -15,6 +15,8 @@ #include "asw_objective_escape.h" #include "triggers.h" #include "datacache/imdlcache.h" +#include "ai_link.h" +#include "asw_alien.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -33,12 +35,10 @@ ConVar asw_batch_interval("asw_batch_interval", "5", FCVAR_CHEAT, "Time between ConVar asw_candidate_interval("asw_candidate_interval", "1.0", FCVAR_CHEAT, "Interval between updating candidate spawning nodes"); ConVar asw_horde_class( "asw_horde_class", "asw_drone", FCVAR_CHEAT, "Alien class used when spawning hordes" ); -// TODO: Notify the director when a horde is all killed? -// - currently you could try to spawn a 2nd horde while the first is still sitting out in the world - CASW_Spawn_Manager::CASW_Spawn_Manager() { - + m_nAwakeAliens = 0; + m_nAwakeDrones = 0; } CASW_Spawn_Manager::~CASW_Spawn_Manager() @@ -87,6 +87,8 @@ ASW_Alien_Class_Entry* CASW_Spawn_Manager::GetAlienClass( int i ) void CASW_Spawn_Manager::LevelInitPreEntity() { + m_nAwakeAliens = 0; + m_nAwakeDrones = 0; // init alien classes for ( int i = 0; i < GetNumAlienClasses(); i++ ) { @@ -109,6 +111,24 @@ void CASW_Spawn_Manager::LevelInitPostEntity() FindEscapeTriggers(); } +void CASW_Spawn_Manager::OnAlienWokeUp( CASW_Alien *pAlien ) +{ + m_nAwakeAliens++; + if ( pAlien && pAlien->Classify() == CLASS_ASW_DRONE ) + { + m_nAwakeDrones++; + } +} + +void CASW_Spawn_Manager::OnAlienSleeping( CASW_Alien *pAlien ) +{ + m_nAwakeAliens--; + if ( pAlien && pAlien->Classify() == CLASS_ASW_DRONE ) + { + m_nAwakeDrones--; + } +} + // finds all trigger_multiples linked to asw_objective_escape entities void CASW_Spawn_Manager::FindEscapeTriggers() { @@ -180,7 +200,7 @@ void CASW_Spawn_Manager::Update() if ( m_vecHordePosition != vec3_origin && ( !m_batchInterval.HasStarted() || m_batchInterval.IsElapsed() ) ) { int iToSpawn = MIN( m_iHordeToSpawn, asw_max_alien_batch.GetInt() ); - int iSpawned = SpawnAlienBatch( asw_horde_class.GetString(), iToSpawn, m_vecHordePosition, m_angHordeAngle, MARINE_NEAR_DISTANCE ); + int iSpawned = SpawnAlienBatch( asw_horde_class.GetString(), iToSpawn, m_vecHordePosition, m_angHordeAngle, 0 ); m_iHordeToSpawn -= iSpawned; if ( m_iHordeToSpawn <= 0 ) { @@ -189,6 +209,10 @@ void CASW_Spawn_Manager::Update() } else if ( iSpawned == 0 ) // if we failed to spawn any aliens, then try to find a new horde location { + if ( asw_director_debug.GetBool() ) + { + Msg( "Horde failed to spawn any aliens, trying new horde position.\n" ); + } if ( !FindHordePosition() ) // if we failed to find a new location, just abort this horde { m_iHordeToSpawn = 0; @@ -198,6 +222,17 @@ void CASW_Spawn_Manager::Update() } m_batchInterval.Start( asw_batch_interval.GetFloat() ); } + else if ( m_vecHordePosition == vec3_origin ) + { + Msg( "Warning: Had horde to spawn but no position, clearing.\n" ); + m_iHordeToSpawn = 0; + ASWDirector()->OnHordeFinishedSpawning(); + } + } + + if ( asw_director_debug.GetBool() ) + { + engine->Con_NPrintf( 14, "SM: Batch interval: %f pos = %f %f %f\n", m_batchInterval.HasStarted() ? m_batchInterval.GetRemainingTime() : -1, VectorExpand( m_vecHordePosition ) ); } if ( m_iAliensToSpawn > 0 ) @@ -209,6 +244,10 @@ void CASW_Spawn_Manager::Update() void CASW_Spawn_Manager::AddAlien() { + // don't stock up more than 10 wanderers at once + if ( m_iAliensToSpawn > 10 ) + return; + m_iAliensToSpawn++; } @@ -236,7 +275,7 @@ bool CASW_Spawn_Manager::SpawnAlientAtRandomNode() Vector vecMins, vecMaxs; GetAlienBounds( szAlienClass, vecMins, vecMaxs ); - int iMaxTries = 10; + int iMaxTries = 1; for ( int i=0 ; iBuildRoute( pNode->GetPosition( CANDIDATE_ALIEN_HULL ), pMarine->GetAbsOrigin(), NULL, 100 ); if ( !pRoute ) + { + if ( asw_director_debug.GetBool() ) + { + NDebugOverlay::Cross3D( pNode->GetOrigin(), 10.0f, 255, 128, 0, true, 20.0f ); + } continue; + } if ( bNorth && UTIL_ASW_DoorBlockingRoute( pRoute, true ) ) { @@ -265,9 +310,27 @@ bool CASW_Spawn_Manager::SpawnAlientAtRandomNode() { if ( SpawnAlienAt( szAlienClass, vecSpawnPos, vec3_angle ) ) { + if ( asw_director_debug.GetBool() ) + { + NDebugOverlay::Cross3D( vecSpawnPos, 25.0f, 255, 255, 255, true, 20.0f ); + float flDist; + CASW_Marine *pMarine = UTIL_ASW_NearestMarine( vecSpawnPos, flDist ); + if ( pMarine ) + { + NDebugOverlay::Line( pMarine->GetAbsOrigin(), vecSpawnPos, 64, 64, 64, true, 60.0f ); + } + } + DeleteRoute( pRoute ); return true; } } + else + { + if ( asw_director_debug.GetBool() ) + { + NDebugOverlay::Cross3D( vecSpawnPos, 25.0f, 255, 0, 0, true, 20.0f ); + } + } DeleteRoute( pRoute ); } return false; @@ -288,7 +351,13 @@ bool CASW_Spawn_Manager::AddHorde( int iHordeSize ) { if ( asw_director_debug.GetBool() ) { - NDebugOverlay::Cross3D( m_vecHordePosition, 50.0f, 255, 128, 0, true, 40.0f ); + NDebugOverlay::Cross3D( m_vecHordePosition, 50.0f, 255, 128, 0, true, 60.0f ); + float flDist; + CASW_Marine *pMarine = UTIL_ASW_NearestMarine( m_vecHordePosition, flDist ); + if ( pMarine ) + { + NDebugOverlay::Line( pMarine->GetAbsOrigin(), m_vecHordePosition, 255, 128, 0, true, 60.0f ); + } } } } @@ -378,11 +447,19 @@ void CASW_Spawn_Manager::UpdateCandidateNodes() if ( vecPos.y >= vecSouthMarine.y ) { + if ( asw_director_debug.GetInt() == 3 ) + { + NDebugOverlay::Box( vecPos, -Vector( 5, 5, 5 ), Vector( 5, 5, 5 ), 32, 32, 128, 10, 60.0f ); + } m_northCandidateNodes.AddToTail( i ); } if ( vecPos.y <= vecNorthMarine.y ) { m_southCandidateNodes.AddToTail( i ); + if ( asw_director_debug.GetInt() == 3 ) + { + NDebugOverlay::Box( vecPos, -Vector( 5, 5, 5 ), Vector( 5, 5, 5 ), 128, 32, 32, 10, 60.0f ); + } } } } @@ -410,9 +487,15 @@ bool CASW_Spawn_Manager::FindHordePosition() CUtlVector &candidateNodes = bNorth ? m_northCandidateNodes : m_southCandidateNodes; if ( candidateNodes.Count() <= 0 ) + { + if ( asw_director_debug.GetBool() ) + { + Msg( " Failed to find horde pos as there are no candidate nodes\n" ); + } return false; + } - int iMaxTries = 10; + int iMaxTries = 3; for ( int i=0 ; i(UTIL_ASW_NearestMarine( pNode->GetPosition( CANDIDATE_ALIEN_HULL ), flDistance )); if ( !pMarine ) + { + if ( asw_director_debug.GetBool() ) + { + Msg( " Failed to find horde pos as there is no nearest marine\n" ); + } return false; + } // check if there's a route from this node to the marine(s) AI_Waypoint_t *pRoute = ASWPathUtils()->BuildRoute( pNode->GetPosition( CANDIDATE_ALIEN_HULL ), pMarine->GetAbsOrigin(), NULL, 100 ); if ( !pRoute ) + { + if ( asw_director_debug.GetInt() >= 2 ) + { + Msg( " Discarding horde node %d as there's no route.\n", iChosen ); + } continue; + } if ( bNorth && UTIL_ASW_DoorBlockingRoute( pRoute, true ) ) { + if ( asw_director_debug.GetInt() >= 2 ) + { + Msg( " Discarding horde node %d as there's a door in the way.\n", iChosen ); + } DeleteRoute( pRoute ); continue; } m_vecHordePosition = pNode->GetPosition( CANDIDATE_ALIEN_HULL ) + Vector( 0, 0, 32 ); + + // spawn facing the nearest marine + Vector vecDir = pMarine->GetAbsOrigin() - m_vecHordePosition; + vecDir.z = 0; + vecDir.NormalizeInPlace(); + VectorAngles( vecDir, m_angHordeAngle ); + + if ( asw_director_debug.GetInt() >= 2 ) + { + Msg( " Accepting horde node %d.\n", iChosen ); + } DeleteRoute( pRoute ); return true; } + if ( asw_director_debug.GetBool() ) + { + Msg( " Failed to find horde pos as we tried 3 times to build routes to possible locations, but failed\n" ); + } + return false; } @@ -485,8 +600,9 @@ bool CASW_Spawn_Manager::GetAlienBounds( string_t iszAlienClass, Vector &vecMins } // spawn a group of aliens at the target point -int CASW_Spawn_Manager::SpawnAlienBatch( const char* szAlienClass, int iNumAliens, const Vector &vecPosition, const QAngle &angle, float flMarinesBeyondDist ) +int CASW_Spawn_Manager::SpawnAlienBatch( const char* szAlienClass, int iNumAliens, const Vector &vecPosition, const QAngle &angFacing, float flMarinesBeyondDist ) { + int iSpawned = 0; bool bCheckGround = true; Vector vecMins = NAI_Hull::Mins(HULL_MEDIUMBIG); @@ -499,7 +615,7 @@ int CASW_Spawn_Manager::SpawnAlienBatch( const char* szAlienClass, int iNumAlien // spawn one in the middle if ( ValidSpawnPoint( vecPosition, vecMins, vecMaxs, bCheckGround, flMarinesBeyondDist ) ) { - if ( SpawnAlienAt( szAlienClass, vecPosition, angle ) ) + if ( SpawnAlienAt( szAlienClass, vecPosition, angFacing ) ) iSpawned++; } @@ -507,6 +623,8 @@ int CASW_Spawn_Manager::SpawnAlienBatch( const char* szAlienClass, int iNumAlien Vector vecNewPos = vecPosition; for ( int i=1; i<=5 && iSpawned < iNumAliens; i++ ) { + QAngle angle = angFacing; + angle[YAW] += RandomFloat( -20, 20 ); // spawn aliens along top of box for ( int x=-i; x<=i && iSpawned < iNumAliens; x++ ) { @@ -591,6 +709,14 @@ CBaseEntity* CASW_Spawn_Manager::SpawnAlienAt(const char* szAlienClass, const Ve return NULL; } + // have drones unburrow by default, so we don't worry so much about them spawning onscreen + if ( !Q_strcmp( szAlienClass, "asw_drone" ) ) + { + pSpawnable->StartBurrowed(); + pSpawnable->SetUnburrowIdleActivity( NULL_STRING ); + pSpawnable->SetUnburrowActivity( NULL_STRING ); + } + DispatchSpawn( pEntity ); pEntity->Activate(); @@ -663,6 +789,308 @@ void CASW_Spawn_Manager::DeleteRoute( AI_Waypoint_t *pWaypointList ) } } +bool CASW_Spawn_Manager::SpawnRandomShieldbug() +{ + int iNumNodes = g_pBigAINet->NumNodes(); + if ( iNumNodes < 6 ) + return false; + + int nHull = HULL_WIDE_SHORT; + CUtlVector aAreas; + for ( int i = 0; i < 6; i++ ) + { + CAI_Node *pNode = NULL; + int nTries = 0; + while ( nTries < 5 && ( !pNode || pNode->GetType() != NODE_GROUND ) ) + { + pNode = g_pBigAINet->GetNode( RandomInt( 0, iNumNodes ) ); + nTries++; + } + + if ( pNode ) + { + CASW_Open_Area *pArea = FindNearbyOpenArea( pNode->GetOrigin(), HULL_MEDIUMBIG ); + if ( pArea && pArea->m_nTotalLinks > 30 ) + { + // test if there's room to spawn a shieldbug at that spot + if ( ValidSpawnPoint( pArea->m_pNode->GetPosition( nHull ), NAI_Hull::Mins( nHull ), NAI_Hull::Maxs( nHull ), true, false ) ) + { + aAreas.AddToTail( pArea ); + } + else + { + delete pArea; + } + } + } + // stop searching once we have 3 acceptable candidates + if ( aAreas.Count() >= 3 ) + break; + } + + // find area with the highest connectivity + CASW_Open_Area *pBestArea = NULL; + for ( int i = 0; i < aAreas.Count(); i++ ) + { + CASW_Open_Area *pArea = aAreas[i]; + if ( !pBestArea || pArea->m_nTotalLinks > pBestArea->m_nTotalLinks ) + { + pBestArea = pArea; + } + } + + if ( pBestArea ) + { + CBaseEntity *pAlien = SpawnAlienAt( "asw_shieldbug", pBestArea->m_pNode->GetPosition( nHull ), RandomAngle( 0, 360 ) ); + IASW_Spawnable_NPC *pSpawnable = dynamic_cast( pAlien ); + if ( pSpawnable ) + { + pSpawnable->SetAlienOrders(AOT_SpreadThenHibernate, vec3_origin, NULL); + } + aAreas.PurgeAndDeleteElements(); + return true; + } + + aAreas.PurgeAndDeleteElements(); + return false; +} + +Vector TraceToGround( const Vector &vecPos ) +{ + trace_t tr; + UTIL_TraceLine( vecPos + Vector( 0, 0, 100 ), vecPos, MASK_NPCSOLID, NULL, ASW_COLLISION_GROUP_PARASITE, &tr ); + return tr.endpos; +} + +bool CASW_Spawn_Manager::SpawnRandomParasitePack( int nParasites ) +{ + int iNumNodes = g_pBigAINet->NumNodes(); + if ( iNumNodes < 6 ) + return false; + + int nHull = HULL_TINY; + CUtlVector aAreas; + for ( int i = 0; i < 6; i++ ) + { + CAI_Node *pNode = NULL; + int nTries = 0; + while ( nTries < 5 && ( !pNode || pNode->GetType() != NODE_GROUND ) ) + { + pNode = g_pBigAINet->GetNode( RandomInt( 0, iNumNodes ) ); + nTries++; + } + + if ( pNode ) + { + CASW_Open_Area *pArea = FindNearbyOpenArea( pNode->GetOrigin(), HULL_MEDIUMBIG ); + if ( pArea && pArea->m_nTotalLinks > 30 ) + { + // test if there's room to spawn a shieldbug at that spot + if ( ValidSpawnPoint( pArea->m_pNode->GetPosition( nHull ), NAI_Hull::Mins( nHull ), NAI_Hull::Maxs( nHull ), true, false ) ) + { + aAreas.AddToTail( pArea ); + } + else + { + delete pArea; + } + } + } + // stop searching once we have 3 acceptable candidates + if ( aAreas.Count() >= 3 ) + break; + } + + // find area with the highest connectivity + CASW_Open_Area *pBestArea = NULL; + for ( int i = 0; i < aAreas.Count(); i++ ) + { + CASW_Open_Area *pArea = aAreas[i]; + if ( !pBestArea || pArea->m_nTotalLinks > pBestArea->m_nTotalLinks ) + { + pBestArea = pArea; + } + } + + if ( pBestArea ) + { + for ( int i = 0; i < nParasites; i++ ) + { + CBaseEntity *pAlien = SpawnAlienAt( "asw_parasite", TraceToGround( pBestArea->m_pNode->GetPosition( nHull ) ), RandomAngle( 0, 360 ) ); + IASW_Spawnable_NPC *pSpawnable = dynamic_cast( pAlien ); + if ( pSpawnable ) + { + pSpawnable->SetAlienOrders(AOT_SpreadThenHibernate, vec3_origin, NULL); + } + if ( asw_director_debug.GetBool() && pAlien ) + { + Msg( "Spawned parasite at %f %f %f\n", pAlien->GetAbsOrigin() ); + NDebugOverlay::Cross3D( pAlien->GetAbsOrigin(), 8.0f, 255, 0, 0, true, 20.0f ); + } + } + aAreas.PurgeAndDeleteElements(); + return true; + } + + aAreas.PurgeAndDeleteElements(); + return false; +} + +// heuristic to find reasonably open space - searches for areas with high node connectivity +CASW_Open_Area* CASW_Spawn_Manager::FindNearbyOpenArea( const Vector &vecSearchOrigin, int nSearchHull ) +{ + CBaseEntity *pStartEntity = gEntList.FindEntityByClassname( NULL, "info_player_start" ); + int iNumNodes = g_pBigAINet->NumNodes(); + CAI_Node *pHighestConnectivity = NULL; + int nHighestLinks = 0; + for ( int i=0 ; iGetNode( i ); + if ( !pNode || pNode->GetType() != NODE_GROUND ) + continue; + + Vector vecPos = pNode->GetOrigin(); + float flDist = vecPos.DistTo( vecSearchOrigin ); + if ( flDist > 400.0f ) + continue; + + // discard if node is too near start location + if ( pStartEntity && vecPos.DistTo( pStartEntity->GetAbsOrigin() ) < 1400.0f ) // NOTE: assumes all start points are clustered near one another + continue; + + // discard if node is inside an escape area + bool bInsideEscapeArea = false; + for ( int d=0; dCollisionProp()->IsPointInBounds( vecPos ) ) + { + bInsideEscapeArea = true; + break; + } + } + if ( bInsideEscapeArea ) + continue; + + // count links that drones could follow + int nLinks = pNode->NumLinks(); + int nValidLinks = 0; + for ( int k = 0; k < nLinks; k++ ) + { + CAI_Link *pLink = pNode->GetLinkByIndex( k ); + if ( !pLink ) + continue; + + if ( !( pLink->m_iAcceptedMoveTypes[nSearchHull] & bits_CAP_MOVE_GROUND ) ) + continue; + + nValidLinks++; + } + if ( nValidLinks > nHighestLinks ) + { + nHighestLinks = nValidLinks; + pHighestConnectivity = pNode; + } + if ( asw_director_debug.GetBool() ) + { + NDebugOverlay::Text( vecPos, UTIL_VarArgs( "%d", nValidLinks ), false, 10.0f ); + } + } + + if ( !pHighestConnectivity ) + return NULL; + + // now, starting at the new node, find all nearby nodes with a minimum connectivity + CASW_Open_Area *pArea = new CASW_Open_Area(); + pArea->m_vecOrigin = pHighestConnectivity->GetOrigin(); + pArea->m_pNode = pHighestConnectivity; + int nMinLinks = nHighestLinks * 0.3f; + nMinLinks = MAX( nMinLinks, 4 ); + + pArea->m_aAreaNodes.AddToTail( pHighestConnectivity ); + if ( asw_director_debug.GetBool() ) + { + Msg( "minLinks = %d\n", nMinLinks ); + } + pArea->m_nTotalLinks = 0; + for ( int i=0 ; iGetNode( i ); + if ( !pNode || pNode->GetType() != NODE_GROUND ) + continue; + + Vector vecPos = pNode->GetOrigin(); + float flDist = vecPos.DistTo( pArea->m_vecOrigin ); + if ( flDist > 400.0f ) + continue; + + // discard if node is inside an escape area + bool bInsideEscapeArea = false; + for ( int d=0; dCollisionProp()->IsPointInBounds( vecPos ) ) + { + bInsideEscapeArea = true; + break; + } + } + if ( bInsideEscapeArea ) + continue; + + // count links that drones could follow + int nLinks = pNode->NumLinks(); + int nValidLinks = 0; + for ( int k = 0; k < nLinks; k++ ) + { + CAI_Link *pLink = pNode->GetLinkByIndex( k ); + if ( !pLink ) + continue; + + if ( !( pLink->m_iAcceptedMoveTypes[nSearchHull] & bits_CAP_MOVE_GROUND ) ) + continue; + + nValidLinks++; + } + if ( nValidLinks >= nMinLinks ) + { + pArea->m_aAreaNodes.AddToTail( pNode ); + pArea->m_nTotalLinks += nValidLinks; + } + } + // highlight and measure bounds + Vector vecAreaMins = Vector( FLT_MAX, FLT_MAX, FLT_MAX ); + Vector vecAreaMaxs = Vector( -FLT_MAX, -FLT_MAX, -FLT_MAX ); + + for ( int i = 0; i < pArea->m_aAreaNodes.Count(); i++ ) + { + vecAreaMins = VectorMin( vecAreaMins, pArea->m_aAreaNodes[i]->GetOrigin() ); + vecAreaMaxs = VectorMax( vecAreaMaxs, pArea->m_aAreaNodes[i]->GetOrigin() ); + + if ( asw_director_debug.GetBool() ) + { + if ( i == 0 ) + { + NDebugOverlay::Cross3D( pArea->m_aAreaNodes[i]->GetOrigin(), 20.0f, 255, 255, 64, true, 10.0f ); + } + else + { + NDebugOverlay::Cross3D( pArea->m_aAreaNodes[i]->GetOrigin(), 10.0f, 255, 128, 0, true, 10.0f ); + } + } + } + + Vector vecArea = ( vecAreaMaxs - vecAreaMins ); + float flArea = vecArea.x * vecArea.y; + + if ( asw_director_debug.GetBool() ) + { + Msg( "area mins = %f %f %f\n", VectorExpand( vecAreaMins ) ); + Msg( "area maxs = %f %f %f\n", VectorExpand( vecAreaMaxs ) ); + NDebugOverlay::Box( vec3_origin, vecAreaMins, vecAreaMaxs, 255, 128, 128, 10, 10.0f ); + Msg( "Total links = %d Area = %f\n", pArea->m_nTotalLinks, flArea ); + } + + return pArea; +} // creates a batch of aliens at the mouse cursor void asw_alien_batch_f( const CCommand& args ) @@ -715,4 +1143,15 @@ void asw_alien_horde_f( const CCommand& args ) Msg("Failed to add horde\n"); } } -static ConCommand asw_alien_horde("asw_alien_horde", asw_alien_horde_f, "Creates a horde of aliens somewhere nearby", FCVAR_GAMEDLL | FCVAR_CHEAT); \ No newline at end of file +static ConCommand asw_alien_horde("asw_alien_horde", asw_alien_horde_f, "Creates a horde of aliens somewhere nearby", FCVAR_GAMEDLL | FCVAR_CHEAT); + + +CON_COMMAND_F( asw_spawn_shieldbug, "Spawns a shieldbug somewhere randomly in the map", FCVAR_CHEAT ) +{ + ASWSpawnManager()->SpawnRandomShieldbug(); +} + +CON_COMMAND_F( asw_spawn_parasite_pack, "Spawns a group of parasites somewhere randomly in the map", FCVAR_CHEAT ) +{ + ASWSpawnManager()->SpawnRandomParasitePack( RandomInt( 3, 5 ) ); +} \ No newline at end of file diff --git a/sdk_src/game/server/swarm/asw_spawn_manager.h b/sdk_src/game/server/swarm/asw_spawn_manager.h index fd1e5dd..db484b1 100644 --- a/sdk_src/game/server/swarm/asw_spawn_manager.h +++ b/sdk_src/game/server/swarm/asw_spawn_manager.h @@ -7,6 +7,8 @@ class CAI_Network; class CTriggerMultiple; struct AI_Waypoint_t; +class CAI_Node; +class CASW_Alien; // The spawn manager can spawn aliens and groups of aliens @@ -20,6 +22,23 @@ public: int m_nHullType; }; +class CASW_Open_Area +{ +public: + CASW_Open_Area() + { + m_flArea = 0.0f; + m_nTotalLinks = 0; + m_vecOrigin = vec3_origin; + m_pNode = NULL; + } + float m_flArea; + int m_nTotalLinks; + Vector m_vecOrigin; + CAI_Node *m_pNode; + CUtlVector m_aAreaNodes; +}; + class CASW_Spawn_Manager { public: @@ -41,11 +60,20 @@ public: bool GetAlienBounds( const char *szAlienClass, Vector &vecMins, Vector &vecMaxs ); bool GetAlienBounds( string_t iszAlienClass, Vector &vecMins, Vector &vecMaxs ); - int GetHordeToSpawn() { return m_iHordeToSpawn; } + int GetHordeToSpawn() { return m_iHordeToSpawn; } + + void OnAlienWokeUp( CASW_Alien *pAlien ); + void OnAlienSleeping( CASW_Alien *pAlien ); + int GetAwakeAliens() { return m_nAwakeAliens; } + int GetAwakeDrones() { return m_nAwakeDrones; } int GetNumAlienClasses(); ASW_Alien_Class_Entry* GetAlienClass( int i ); + // spawns a shieldbug somewhere randomly in the map + bool SpawnRandomShieldbug(); + bool SpawnRandomParasitePack( int nParasites ); + private: void UpdateCandidateNodes(); bool FindHordePosition(); @@ -54,12 +82,18 @@ private: void FindEscapeTriggers(); void DeleteRoute( AI_Waypoint_t *pWaypointList ); + // finds an area with good node connectivity. Caller should take ownership of the CASW_Open_Area instance. + CASW_Open_Area* FindNearbyOpenArea( const Vector &vecSearchOrigin, int nSearchHull ); + CountdownTimer m_batchInterval; Vector m_vecHordePosition; QAngle m_angHordeAngle; int m_iHordeToSpawn; int m_iAliensToSpawn; + int m_nAwakeAliens; + int m_nAwakeDrones; + // maintaining a list of possible nodes to spawn aliens from CUtlVector m_northCandidateNodes; CUtlVector m_southCandidateNodes; diff --git a/sdk_src/game/server/swarm/asw_spawner.cpp b/sdk_src/game/server/swarm/asw_spawner.cpp index 3822c82..6941112 100644 --- a/sdk_src/game/server/swarm/asw_spawner.cpp +++ b/sdk_src/game/server/swarm/asw_spawner.cpp @@ -153,6 +153,9 @@ void CASW_Spawner::AlienKilled( CBaseEntity *pVictim ) m_nCurrentLiveAliens--; + if (asw_debug_spawners.GetBool()) + Msg("%d AlienKilled NumLive = %d\n", entindex(), m_nCurrentLiveAliens ); + // If we're here, we're getting erroneous death messages from children we haven't created AssertMsg( m_nCurrentLiveAliens >= 0, "asw_spawner receiving child death notice but thinks has no children\n" ); @@ -293,6 +296,22 @@ bool CASW_Spawner::ApplyCarnageMode( float fScaler, float fInvScaler ) return false; } +int CASW_Spawner::DrawDebugTextOverlays() +{ + int text_offset = BaseClass::DrawDebugTextOverlays(); + + if (m_debugOverlays & OVERLAY_TEXT_BIT) + { + NDebugOverlay::EntityText( entindex(),text_offset,CFmtStr( "Num Live Aliens: %d", m_nCurrentLiveAliens ),0 ); + text_offset++; + NDebugOverlay::EntityText( entindex(),text_offset,CFmtStr( "Max Live Aliens: %d", m_nMaxLiveAliens ),0 ); + text_offset++; + NDebugOverlay::EntityText( entindex(),text_offset,CFmtStr( "Alien supply: %d", m_bInfiniteAliens ? -1 : m_nNumAliens ),0 ); + text_offset++; + } + return text_offset; +} + void ASW_ApplyCarnage_f(float fScaler) { if ( fScaler <= 0 ) diff --git a/sdk_src/game/server/swarm/asw_spawner.h b/sdk_src/game/server/swarm/asw_spawner.h index 0afa31b..34f4a25 100644 --- a/sdk_src/game/server/swarm/asw_spawner.h +++ b/sdk_src/game/server/swarm/asw_spawner.h @@ -21,6 +21,7 @@ public: virtual void Spawn(); virtual void Precache(); virtual void InitAlienClassName(); + virtual int DrawDebugTextOverlays(); Class_T Classify() { return (Class_T) CLASS_ASW_SPAWNER; } diff --git a/sdk_src/game/shared/SoundEmitterSystem.cpp b/sdk_src/game/shared/SoundEmitterSystem.cpp index 3ae7e10..96d0c2d 100644 --- a/sdk_src/game/shared/SoundEmitterSystem.cpp +++ b/sdk_src/game/shared/SoundEmitterSystem.cpp @@ -1208,6 +1208,9 @@ void SoundSystemPreloadSounds( void ) CON_COMMAND( sv_soundemitter_flush, "Flushes the sounds.txt system (server only)" ) { + if ( !UTIL_IsCommandIssuedByServerAdmin() ) + return; + // save the current soundscape // kill the system g_SoundEmitterSystem.Flush(); @@ -1222,12 +1225,18 @@ CON_COMMAND( sv_soundemitter_flush, "Flushes the sounds.txt system (server only) CON_COMMAND( sv_soundemitter_filecheck, "Report missing wave files for sounds and game_sounds files." ) { + if ( !UTIL_IsCommandIssuedByServerAdmin() ) + return; + int missing = soundemitterbase->CheckForMissingWavFiles( true ); DevMsg( "---------------------------\nTotal missing files %i\n", missing ); } CON_COMMAND( sv_findsoundname, "Find sound names which reference the specified wave files." ) -{ +{ + if ( !UTIL_IsCommandIssuedByServerAdmin() ) + return; + if ( args.ArgC() != 2 ) return; diff --git a/sdk_src/game/shared/ai_responsesystem.cpp b/sdk_src/game/shared/ai_responsesystem.cpp index e8c4a43..71d4a1a 100644 --- a/sdk_src/game/shared/ai_responsesystem.cpp +++ b/sdk_src/game/shared/ai_responsesystem.cpp @@ -639,6 +639,11 @@ IResponseSystem *g_pResponseSystem = &defaultresponsesytem; CON_COMMAND( rr_reloadresponsesystems, "Reload all response system scripts." ) { +#ifdef GAME_DLL + if ( !UTIL_IsCommandIssuedByServerAdmin() ) + return; +#endif + defaultresponsesytem.ReloadAllResponseSystems(); } diff --git a/sdk_src/game/shared/swarm/asw_achievements.cpp b/sdk_src/game/shared/swarm/asw_achievements.cpp index 2a83d94..02fa1d0 100644 --- a/sdk_src/game/shared/swarm/asw_achievements.cpp +++ b/sdk_src/game/shared/swarm/asw_achievements.cpp @@ -762,7 +762,7 @@ class CAchievement_Unlock_All_Weapons : public CASW_Achievement { if ( !Q_stricmp( event->GetName(), "level_up" ) ) { - if ( event->GetInt( "level" ) >= ASW_LEVEL_CAP ) + if ( event->GetInt( "level" ) >= ASW_NUM_EXPERIENCE_LEVELS ) { IncrementCount(); } @@ -1077,5 +1077,73 @@ class CAchievement_Para_Hat : public CASW_Achievement }; DECLARE_ACHIEVEMENT_ORDER( CAchievement_Para_Hat, ACHIEVEMENT_ASW_PARA_HAT, "ASW_PARA_HAT", 5, 149 ); +class CAchievement_Imba_Campaign : public CASW_Achievement +{ + void Init() + { + SetFlags( ACH_SAVE_GLOBAL | ACH_HAS_COMPONENTS ); + SetStoreProgressInSteam( true ); + SetGoal( NELEMS( g_szAchievementMapNames ) ); + } + virtual void ListenForEvents( void ) + { + ListenForGameEvent( "mission_success" ); + } + + void FireGameEvent_Internal( IGameEvent *event ) + { + if ( !Q_stricmp( event->GetName(), "mission_success" ) && ASWGameRules() && ASWGameRules()->GetSkillLevel() >= 5 ) + { + if ( LocalPlayerWasSpectating() ) + return; + + const char *szMapName = event->GetString( "strMapName" ); + for ( int i = 0; i < NELEMS( g_szAchievementMapNames ); i++ ) + { + if ( !Q_stricmp( szMapName, g_szAchievementMapNames[i] ) ) + { + EnsureComponentBitSetAndEvaluate( i ); + break; + } + } + } + } +}; +DECLARE_ACHIEVEMENT_ORDER( CAchievement_Imba_Campaign, ACHIEVEMENT_ASW_IMBA_CAMPAIGN, "ASW_IMBA_CAMPAIGN", 5, 182 ); + +class CAchievement_Hardcore : public CASW_Achievement +{ + void Init() + { + SetFlags( ACH_SAVE_GLOBAL ); + SetGoal( 1 ); + } + + virtual void ListenForEvents( void ) + { + ListenForGameEvent( "mission_success" ); + } + + void FireGameEvent_Internal( IGameEvent *event ) + { + if ( !Q_stricmp( event->GetName(), "mission_success" ) && ASWGameRules() && ASWGameRules()->GetSkillLevel() >= 5 + && CAlienSwarm::IsHardcoreFF() && CAlienSwarm::IsOnslaught() ) + { + if ( LocalPlayerWasSpectating() ) + return; + + const char *szMapName = event->GetString( "strMapName" ); + for ( int i = 0; i < NELEMS( g_szAchievementMapNames ); i++ ) + { + if ( !Q_stricmp( szMapName, g_szAchievementMapNames[i] ) ) + { + IncrementCount(); + break; + } + } + } + } +}; +DECLARE_ACHIEVEMENT_ORDER( CAchievement_Hardcore, ACHIEVEMENT_ASW_HARDCORE, "ASW_HARDCORE", 5, 184 ); #endif \ No newline at end of file diff --git a/sdk_src/game/shared/swarm/asw_achievements.h b/sdk_src/game/shared/swarm/asw_achievements.h index 5498bc2..02e5584 100644 --- a/sdk_src/game/shared/swarm/asw_achievements.h +++ b/sdk_src/game/shared/swarm/asw_achievements.h @@ -68,7 +68,9 @@ enum ACHIEVEMENT_ASW_SPEEDRUN_TIMOR, ACHIEVEMENT_ASW_CAMPAIGN_NO_DEATHS, ACHIEVEMENT_ASW_MISSION_NO_DEATHS, - ACHIEVEMENT_ASW_PARA_HAT + ACHIEVEMENT_ASW_PARA_HAT, + ACHIEVEMENT_ASW_IMBA_CAMPAIGN, + ACHIEVEMENT_ASW_HARDCORE, }; #define ACH_LISTEN_ALIEN_DEATH_EVENTS 0x1000 diff --git a/sdk_src/game/shared/swarm/asw_gamerules.cpp b/sdk_src/game/shared/swarm/asw_gamerules.cpp index ca51269..87f95f4 100644 --- a/sdk_src/game/shared/swarm/asw_gamerules.cpp +++ b/sdk_src/game/shared/swarm/asw_gamerules.cpp @@ -84,6 +84,7 @@ #include "EntityFlame.h" #include "asw_buffgrenade_projectile.h" #include "asw_achievements.h" + #include "asw_director.h" #endif #include "game_timescale_shared.h" #include "asw_gamerules.h" @@ -135,6 +136,19 @@ extern ConVar old_radius_damage; ConVar sv_vote_kick_ban_duration("sv_vote_kick_ban_duration", "5", 0, "How long should a kick vote ban someone from the server? (in minutes)"); ConVar sv_timeout_when_fully_connected( "sv_timeout_when_fully_connected", "30", FCVAR_NONE, "Once fully connected, player will be kicked if he doesn't send a network message within this interval." ); ConVar mm_swarm_state( "mm_swarm_state", "ingame", FCVAR_DEVELOPMENTONLY ); + + static void UpdateMatchmakingTagsCallback( IConVar *pConVar, const char *pOldValue, float flOldValue ) + { + // update sv_tags to force an update of the matchmaking tags + static ConVarRef sv_tags( "sv_tags" ); + + if ( sv_tags.IsValid() ) + { + char buffer[ 1024 ]; + Q_snprintf( buffer, sizeof( buffer ), "%s", sv_tags.GetString() ); + sv_tags.SetValue( buffer ); + } + } #else extern ConVar asw_controls; #endif @@ -153,6 +167,30 @@ ConVar asw_stim_time_scale("asw_stim_time_scale", "0.35", FCVAR_REPLICATED | FCV ConVar asw_time_scale_delay("asw_time_scale_delay", "0.15", FCVAR_REPLICATED | FCVAR_CHEAT, "Delay before timescale changes to give a chance for the client to comply and predict."); ConVar asw_ignore_need_two_player_requirement("asw_ignore_need_two_player_requirement", "0", FCVAR_REPLICATED, "If set to 1, ignores the mission setting that states two players are needed to start the mission."); ConVar mp_gamemode( "mp_gamemode", "campaign", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "Current game mode, acceptable values are campaign and single_mission.", false, 0.0f, false, 0.0f ); +ConVar asw_sentry_friendly_fire_scale( "asw_sentry_friendly_fire_scale", "0", FCVAR_REPLICATED, "Damage scale for sentry gun friendly fire" +#ifdef GAME_DLL + ,UpdateMatchmakingTagsCallback ); +#else + ); +#endif +ConVar asw_marine_ff_absorption("asw_marine_ff_absorption", "1", FCVAR_REPLICATED, "Friendly fire absorption style (0=none 1=ramp up 2=ramp down)" +#ifdef GAME_DLL + ,UpdateMatchmakingTagsCallback ); +#else + ); +#endif +ConVar asw_horde_override( "asw_horde_override", "0", FCVAR_REPLICATED, "Forces hordes to spawn" +#ifdef GAME_DLL + ,UpdateMatchmakingTagsCallback ); +#else + ); +#endif +ConVar asw_wanderer_override( "asw_wanderer_override", "0", FCVAR_REPLICATED, "Forces wanderers to spawn" +#ifdef GAME_DLL + ,UpdateMatchmakingTagsCallback ); +#else + ); +#endif // ASW Weapons // Rifle @@ -251,7 +289,7 @@ ConVar asw_vote_kick_fraction("asw_vote_kick_fraction", "0.6", FCVAR_REPLICATED ConVar asw_vote_leader_fraction("asw_vote_leader_fraction", "0.6", FCVAR_REPLICATED | FCVAR_ARCHIVE, "Fraction of players needed to activate a leader vote"); ConVar asw_vote_map_fraction("asw_vote_map_fraction", "0.6", FCVAR_REPLICATED | FCVAR_ARCHIVE, "Fraction of players needed to activate a map vote"); ConVar asw_marine_collision("asw_marine_collision", "0", FCVAR_REPLICATED, "Whether marines collide with each other or not, in a multiplayer game"); -ConVar asw_skill( "asw_skill","2", FCVAR_REPLICATED | FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX, "Game skill level (1-4).", true, 1, true, 4 ); +ConVar asw_skill( "asw_skill","2", FCVAR_REPLICATED | FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX, "Game skill level (1-5).", true, 1, true, 5 ); ConVar asw_money( "asw_money", "0", FCVAR_REPLICATED, "Can players collect money?" ); ConVar asw_client_build_maps("asw_client_build_maps", "0", FCVAR_REPLICATED, "Whether clients compile random maps rather than getting sent them"); @@ -456,7 +494,7 @@ CAmmoDef *GetAmmoDef() def.AddAmmoType("ASW_P", DMG_BULLET, TRACER_LINE_AND_WHIZ, "sk_plr_dmg_asw_p", "sk_npc_dmg_asw_p", "sk_max_asw_p", BULLET_IMPULSE(200, 1225), 0 ); // mining laser def.AddAmmoType("ASW_ML", DMG_ENERGYBEAM, TRACER_LINE_AND_WHIZ, "sk_plr_dmg_asw_ml", "sk_npc_dmg_asw_ml", "sk_max_asw_ml", BULLET_IMPULSE(200, 1225), 0 ); - // mining laser + // tesla gun - happy LJ? def.AddAmmoType("ASW_TG", DMG_ENERGYBEAM, TRACER_LINE_AND_WHIZ, "sk_plr_dmg_asw_tg", "sk_npc_dmg_asw_tg", "sk_max_asw_tg", BULLET_IMPULSE(200, 1225), 0 ); // railgun def.AddAmmoType("ASW_RG", DMG_SONIC, TRACER_LINE_AND_WHIZ, "sk_plr_dmg_asw_rg", "sk_npc_dmg_asw_rg", "sk_max_asw_rg", BULLET_IMPULSE(200, 1225), 0 ); @@ -627,19 +665,6 @@ const char * GenerateNewSaveGameName() return NULL; } -void UpdateMatchmakingTags() -{ - // update sv_tags to force an update of the matchmaking tags - static ConVarRef sv_tags( "sv_tags" ); - - if ( sv_tags.IsValid() ) - { - char buffer[ 1024 ]; - Q_snprintf( buffer, sizeof( buffer ), "%s", sv_tags.GetString() ); - sv_tags.SetValue( buffer ); - } -} - CAlienSwarm::CAlienSwarm() { Msg("CAlienSwarm created\n"); @@ -1610,6 +1635,11 @@ void CAlienSwarm::StartMission() m_Medals.OnStartMission(); + if ( ASWDirector() ) + { + ASWDirector()->OnMissionStarted(); + } + Msg("==STARTMISSION==\n"); SetGameState(ASW_GS_LAUNCHING); @@ -3319,7 +3349,7 @@ void CAlienSwarm::MissionComplete( bool bSuccess ) if (!MapScores()->IsModeUnlocked(mapName, GetSkillLevel(), ASW_SM_CARNAGE)) { // check for unlocking carnage (if we completed the mission on Insane) - bJustUnlockedCarnage = (GetSkillLevel() == 4); + bJustUnlockedCarnage = (GetSkillLevel() >= 4); //Msg("Checked just carnage unlock = %d\n", bJustUnlockedCarnage); } if (!MapScores()->IsModeUnlocked(mapName, GetSkillLevel(), ASW_SM_UBER)) @@ -5144,6 +5174,11 @@ void CAlienSwarm::OnSkillLevelChanged( int iNewLevel ) m_iMissionDifficulty = 10; szDifficulty = "insane"; } + else if (iNewLevel == 5) // imba + { + m_iMissionDifficulty = 13; + szDifficulty = "imba"; + } else // normal { m_iMissionDifficulty = 5; @@ -5200,7 +5235,7 @@ void CAlienSwarm::OnSkillLevelChanged( int iNewLevel ) } } - UpdateMatchmakingTags(); + UpdateMatchmakingTagsCallback( NULL, "0", 0.0f ); m_iSkillLevel = iNewLevel; } @@ -5226,12 +5261,28 @@ void CAlienSwarm::RequestSkill( CASW_Player *pPlayer, int nSkill ) if ( !( m_iGameState == ASW_GS_BRIEFING || m_iGameState == ASW_GS_DEBRIEF ) ) // don't allow skill change outside of briefing return; - if ( nSkill >= 1 && nSkill <= 4 && ASWGameResource() && ASWGameResource()->GetLeader() == pPlayer ) + if ( nSkill >= 1 && nSkill <= 5 && ASWGameResource() && ASWGameResource()->GetLeader() == pPlayer ) { ConVar *var = (ConVar *)cvar->FindVar( "asw_skill" ); if (var) { + int iOldSkill = var->GetInt(); + var->SetValue( nSkill ); + + if ( iOldSkill != var->GetInt() ) + { + CReliableBroadcastRecipientFilter filter; + filter.RemoveRecipient( pPlayer ); // notify everyone except the player changing the difficulty level + switch(var->GetInt()) + { + case 1: UTIL_ClientPrintFilter( filter, ASW_HUD_PRINTTALKANDCONSOLE, "#asw_set_difficulty_easy", pPlayer->GetPlayerName() ); break; + case 2: UTIL_ClientPrintFilter( filter, ASW_HUD_PRINTTALKANDCONSOLE, "#asw_set_difficulty_normal", pPlayer->GetPlayerName() ); break; + case 3: UTIL_ClientPrintFilter( filter, ASW_HUD_PRINTTALKANDCONSOLE, "#asw_set_difficulty_hard", pPlayer->GetPlayerName() ); break; + case 4: UTIL_ClientPrintFilter( filter, ASW_HUD_PRINTTALKANDCONSOLE, "#asw_set_difficulty_insane", pPlayer->GetPlayerName() ); break; + case 5: UTIL_ClientPrintFilter( filter, ASW_HUD_PRINTTALKANDCONSOLE, "#asw_set_difficulty_imba", pPlayer->GetPlayerName() ); break; + } + } } } } @@ -5241,7 +5292,7 @@ void CAlienSwarm::RequestSkillUp(CASW_Player *pPlayer) if (m_iGameState != ASW_GS_BRIEFING) // don't allow skill change outside of briefing return; - if (m_iSkillLevel < 4 && ASWGameResource() && ASWGameResource()->GetLeader() == pPlayer) + if (m_iSkillLevel < 5 && ASWGameResource() && ASWGameResource()->GetLeader() == pPlayer) { ConVar *var = (ConVar *)cvar->FindVar( "asw_skill" ); if (var) @@ -6598,6 +6649,8 @@ int CAlienSwarm::TotalInfestDamage() return 270; case 4: return 280; // BARELY survivable with Bastille and heal beacon + case 5: + return 280; } // ASv1 Total infest damage = 90 + difficulty * 20; @@ -6749,3 +6802,13 @@ const QAngle& CAlienSwarm::GetTopDownMovementAxis() static QAngle axis = ASW_MOVEMENT_AXIS; return axis; } + +bool CAlienSwarm::IsHardcoreFF() +{ + return ( asw_marine_ff_absorption.GetInt() != 1 || asw_sentry_friendly_fire_scale.GetFloat() != 0.0f ); +} + +bool CAlienSwarm::IsOnslaught() +{ + return ( asw_horde_override.GetBool() || asw_wanderer_override.GetBool() ); +} \ No newline at end of file diff --git a/sdk_src/game/shared/swarm/asw_gamerules.h b/sdk_src/game/shared/swarm/asw_gamerules.h index e432ef2..73f85b8 100644 --- a/sdk_src/game/shared/swarm/asw_gamerules.h +++ b/sdk_src/game/shared/swarm/asw_gamerules.h @@ -234,9 +234,9 @@ public: { iLevel = 1; } - else if ( iLevel > 4 ) + else if ( iLevel > 5 ) { - iLevel = 4; + iLevel = 5; } m_iSkillLevel = iLevel; @@ -386,8 +386,8 @@ public: virtual void RefreshSkillData ( bool forceUpdate ); // difficulty - virtual int GetSkillLevel() { return m_iSkillLevel; } // skill level (expanded HL2 style: 1 = easy, 2 = normal, 3 = hard, 4 = insane ) - CNetworkVar(int, m_iSkillLevel); // 1 = easy, 2 = normal, 3 = hard, 4 = insane + virtual int GetSkillLevel() { return m_iSkillLevel; } // skill level (expanded HL2 style: 1 = easy, 2 = normal, 3 = hard, 4 = insane, 5 = imba ) + CNetworkVar(int, m_iSkillLevel); // 1 = easy, 2 = normal, 3 = hard, 4 = insane, 5 = imba int GetMissionDifficulty() { return m_iMissionDifficulty; } // overall difficulty of the mission from 2-10, based on skill level and campaign modifier CNetworkVar(int, m_iMissionDifficulty); CNetworkVar(bool, m_bCheated); @@ -475,6 +475,8 @@ public: bool IsIntroMap() { return m_bIsIntro; } bool IsOutroMap() { return m_bIsOutro; } bool IsLobbyMap() { return m_bIsLobby; } + static bool IsHardcoreFF(); + static bool IsOnslaught(); bool m_bIsTutorial; bool m_bIsIntro; diff --git a/sdk_src/game/shared/swarm/asw_marine_shared.cpp b/sdk_src/game/shared/swarm/asw_marine_shared.cpp index bfa1e61..82e15af 100644 --- a/sdk_src/game/shared/swarm/asw_marine_shared.cpp +++ b/sdk_src/game/shared/swarm/asw_marine_shared.cpp @@ -296,6 +296,7 @@ float CASW_Marine::MaxSpeed() // adjust the speed by difficulty level switch (ASWGameRules()->GetSkillLevel()) { + case 5: speedscale *= asw_marine_speed_scale_insane.GetFloat(); break; case 4: speedscale *= asw_marine_speed_scale_insane.GetFloat(); break; case 3: speedscale *= asw_marine_speed_scale_hard.GetFloat(); break; case 2: speedscale *= asw_marine_speed_scale_normal.GetFloat(); break; diff --git a/sdk_src/game/shared/swarm/asw_player_experience.cpp b/sdk_src/game/shared/swarm/asw_player_experience.cpp index 85c2b71..c877504 100644 --- a/sdk_src/game/shared/swarm/asw_player_experience.cpp +++ b/sdk_src/game/shared/swarm/asw_player_experience.cpp @@ -100,15 +100,28 @@ int g_iXPAward[ ASW_NUM_XP_TYPES ]= 50, // ASW_XP_HACKING }; +// scalar applied to XP required to level, based on your current promotion +float g_flPromotionXPScale[ ASW_PROMOTION_CAP + 1 ]= +{ + 1.0f, + 1.0f, + 1.0f, + 1.0f, + 2.0f, + 4.0f, + 6.0f, +}; + #define ASW_MISSION_XP_AWARD_ON_FAILURE 750 // XP award divided up between objectives // NOTE: If you change this, update the labels in CExperienceReport::OnThink too -float g_flXPDifficultyScale[4]= +float g_flXPDifficultyScale[5]= { 0.5f, // easy 1.0f, // normal 1.2f, // hard 1.4f, // insane + 1.5f, // imba }; // Weapon unlocks @@ -153,17 +166,18 @@ ASW_Weapon_Unlock g_WeaponUnlocks[]= ASW_Weapon_Unlock( "asw_weapon_night_vision", 23 ),// ASW_Weapon_Unlock( "asw_weapon_sentry_cannon", 24 ), ASW_Weapon_Unlock( "asw_weapon_smart_bomb", 25 ),// - ASW_Weapon_Unlock( "asw_weapon_grenade_launcher", 26 ), // ASW_LEVEL_CAP + ASW_Weapon_Unlock( "asw_weapon_grenade_launcher", 26 ), // ASW_NUM_EXPERIENCE_LEVELS }; // given an Experience total, this tells you the player's level -int LevelFromXP( int iExperience ) +int LevelFromXP( int iExperience, int iPromotion ) { - iExperience = MIN( iExperience, ASW_XP_CAP ); + iExperience = MIN( iExperience, ASW_XP_CAP * g_flPromotionXPScale[ iPromotion ] ); for ( int i = 0; i < NELEMS( g_iLevelExperience ); i++ ) { - if ( iExperience < g_iLevelExperience[ i ] ) + int iRequiredXP = (int) ( g_flPromotionXPScale[ iPromotion ] * g_iLevelExperience[ i ] ); + if ( iExperience < iRequiredXP ) { return i; } @@ -368,13 +382,13 @@ void CASW_Player::CalculateEarnedXP() int CASW_Player::GetLevel() { - return LevelFromXP( GetExperience() ); + return LevelFromXP( GetExperience(), GetPromotion() ); } int CASW_Player::GetLevelBeforeDebrief() { - return LevelFromXP( GetExperienceBeforeDebrief() ); + return LevelFromXP( GetExperienceBeforeDebrief(), GetPromotion() ); } void CASW_Player::RequestExperience() @@ -460,7 +474,7 @@ void CASW_Player::AwardExperience() Msg( "%s: AwardExperience: Pre XP is %d\n", IsServerDll() ? "S" : "C", m_iExperience ); m_iExperience += m_iEarnedXP[ ASW_XP_TOTAL ]; - m_iExperience = MIN( m_iExperience, ASW_XP_CAP ); + m_iExperience = MIN( m_iExperience, ASW_XP_CAP * g_flPromotionXPScale[ GetPromotion() ] ); #ifdef CLIENT_DLL if ( IsLocalPlayer() ) @@ -646,7 +660,7 @@ void CASW_Player::Steam_OnUserStatsReceived( UserStatsReceived_t *pUserStatsRece if( GetLocalASWPlayer() == this ) g_ASW_Steamstats.FetchStats( steamID, this ); - if ( IsLocalPlayer() && GetLevel() >= ASW_LEVEL_CAP ) + if ( IsLocalPlayer() && GetLevel() >= ASW_NUM_EXPERIENCE_LEVELS ) { CAchievementMgr *pAchievementMgr = dynamic_cast( engine->GetAchievementMgr() ); if ( !pAchievementMgr ) @@ -694,7 +708,7 @@ ConCommand asw_debug_xp( "asw_debug_xp", asw_debug_xp_f, "Lists XP details for l void CASW_Player::AcceptPromotion() { - if ( GetExperience() < ASW_XP_CAP ) + if ( GetExperience() < ASW_XP_CAP * g_flPromotionXPScale[ GetPromotion() ] ) return; if ( GetPromotion() >= ASW_PROMOTION_CAP ) diff --git a/sdk_src/game/shared/swarm/asw_player_shared.h b/sdk_src/game/shared/swarm/asw_player_shared.h index ba14f04..45fd371 100644 --- a/sdk_src/game/shared/swarm/asw_player_shared.h +++ b/sdk_src/game/shared/swarm/asw_player_shared.h @@ -26,11 +26,13 @@ enum ASW_USE_HOLD_RELEASE_FULL, }; +#define ASW_PROMOTION_CAP 6 #define ASW_NUM_EXPERIENCE_LEVELS 26 extern int g_iLevelExperience[ ASW_NUM_EXPERIENCE_LEVELS ]; +extern float g_flPromotionXPScale[ ASW_PROMOTION_CAP + 1 ]; -int LevelFromXP( int iExperience ); +int LevelFromXP( int iExperience, int iPromotion ); enum CASW_Earned_XP_t { diff --git a/sdk_src/game/shared/swarm/asw_shareddefs.h b/sdk_src/game/shared/swarm/asw_shareddefs.h index f05261e..e511131 100644 --- a/sdk_src/game/shared/swarm/asw_shareddefs.h +++ b/sdk_src/game/shared/swarm/asw_shareddefs.h @@ -583,8 +583,6 @@ enum }; #define ASW_XP_CAP 42250 -#define ASW_LEVEL_CAP 26 -#define ASW_PROMOTION_CAP 3 extern ConVar asw_visrange_generic; @@ -602,4 +600,13 @@ public: #endif +enum CASW_Flock_Leader_State +{ + ASW_FL_CHASING = 0, + ASW_FL_CHARGING, + ASW_FL_FLEEING, + + NUM_FLOCK_LEADER_STATES, +}; + #endif // ASW_SHAREDDEFS_H diff --git a/sdk_src/game/shared/swarm/asw_util_shared.cpp b/sdk_src/game/shared/swarm/asw_util_shared.cpp index cdad33f..771a31c 100644 --- a/sdk_src/game/shared/swarm/asw_util_shared.cpp +++ b/sdk_src/game/shared/swarm/asw_util_shared.cpp @@ -954,7 +954,7 @@ float UTIL_ASW_CalcFastDoorHackTime(int iNumRows, int iNumColumns, int iNumWires ideal_time *= 1.05f; // 5% slower on easy mode else if (iSkill == 3) ideal_time *= 0.95f; // 5% faster on hard mode - else if (iSkill == 4) + else if (iSkill == 4 || iSkill == 5) ideal_time *= 0.90f; // 10% faster on insane mode return ideal_time; diff --git a/sdk_src/game/shared/swarm/asw_weapon_shared.cpp b/sdk_src/game/shared/swarm/asw_weapon_shared.cpp index 468d709..6ce17ac 100644 --- a/sdk_src/game/shared/swarm/asw_weapon_shared.cpp +++ b/sdk_src/game/shared/swarm/asw_weapon_shared.cpp @@ -885,6 +885,7 @@ bool CASW_Weapon::ASWReload( int iClipSize1, int iClipSize2, int iActivity ) case 2: flFastReloadWidth = random->RandomFloat( 0.10f, 0.1f ); break; // easy/normal case 3: flFastReloadWidth = random->RandomFloat( 0.08f, 0.12f ); break; // hard case 4: flFastReloadWidth = random->RandomFloat( 0.06f, 0.10f ); break; // insane + case 5: flFastReloadWidth = random->RandomFloat( 0.055f, 0.09f ); break; // imba } // scale by marine skills flFastReloadWidth *= MarineSkills()->GetSkillBasedValueByMarine( pMarine, ASW_MARINE_SKILL_RELOADING, ASW_MARINE_SUBSKILL_RELOADING_FAST_WIDTH_SCALE );