diff --git a/reactivedrop/resource/ui/basemodui/reactivedropchallengeselection.res b/reactivedrop/resource/ui/basemodui/reactivedropchallengeselection.res new file mode 100644 index 000000000..813c15e19 --- /dev/null +++ b/reactivedrop/resource/ui/basemodui/reactivedropchallengeselection.res @@ -0,0 +1,196 @@ +"Resource/UI/BaseModUI/ReactiveDropChallengeSelection.res" +{ + "ReactiveDropChallengeSelection" + { + "ControlName" "Frame" + "fieldName" "ReactiveDropChallengeSelection" + + "xpos" "0" + "ypos" "0" + "zpos" "50" + "wide" "f0" + "tall" "f0" + "autoResize" "0" + "pinCorner" "0" + "visible" "1" + "enabled" "1" + "tabPosition" "0" + } + + "Title" + { + "fieldName" "Title" + "xpos" "c-266" + "ypos" "56" + "wide" "150" + "tall" "19" + "zpos" "5" + "font" "DefaultExtraLarge" + "textAlignment" "west" + "ControlName" "Label" + "labelText" "#rd_challenge_selection_title" + "fgcolor_override" "224 224 224 255" + } + + "Divider1" + { + "ControlName" "ImagePanel" + "fieldName" "Divider1" + "xpos" "c-238" + "ypos" "115" + "zpos" "2" + "wide" "450" + "tall" "2" + "autoResize" "0" + "pinCorner" "0" + "visible" "0" + "enabled" "1" + "tabPosition" "0" + "image" "divider_gradient" + "scaleImage" "1" + } + + "TxtSearch" + { + "ControlName" "ReactiveDropChallengeSelectionSearch" + "fieldName" "TxtSearch" + "xpos" "c-226" + "ypos" "88" + "wide" "160" + "tall" "15" + "visible" "1" + "enabled" "1" + } + + "GplChallenges" + { + "ControlName" "GenericPanelList" + "fieldName" "GplChallenges" + "xpos" "c-226" + "ypos" "115" + "wide" "450" + "tall" "185" + "zpos" "1" + "autoResize" "1" + "pinCorner" "0" + "visible" "1" + "enabled" "1" + "tabPosition" "1" + "proportionalToParent" "1" + + "bgcolor_override" "0 0 0 128" + } + + "Divider2" + { + "ControlName" "ImagePanel" + "fieldName" "Divider2" + "xpos" "c-238" + "ypos" "297" + "zpos" "2" + "wide" "450" + "tall" "2" + "autoResize" "0" + "pinCorner" "0" + "visible" "0" + "enabled" "1" + "tabPosition" "0" + "image" "divider_gradient" + "scaleImage" "1" + } + + "LblName" + { + "ControlName" "Label" + "fieldName" "LblName" + "xpos" "c-140" + "ypos" "315" + "wide" "265" + "tall" "18" + "zpos" "1" + "autoResize" "0" + "pinCorner" "0" + "visible" "1" + "enabled" "1" + "tabPosition" "0" + "font" "DefaultLarge" + "textAlignment" "north-west" + } + + "LblAuthor" + { + "ControlName" "Label" + "fieldName" "LblAuthor" + "xpos" "c+45" + "ypos" "337" + "wide" "180" + "tall" "15" + "zpos" "1" + "autoResize" "0" + "pinCorner" "0" + "visible" "1" + "enabled" "1" + "tabPosition" "0" + "Font" "DefaultMedium" + "textAlignment" "north-east" + "wrap" "0" + "dulltext" "1" + } + + "LblDescription" + { + "ControlName" "Label" + "fieldName" "LblDescription" + "xpos" "c-140" + "ypos" "355" + "zpos" "1" + "wide" "365" + "tall" "75" + "autoResize" "0" + "pinCorner" "0" + "visible" "1" + "enabled" "1" + "tabPosition" "0" + "Font" "Default" + "textAlignment" "north-west" + "wrap" "1" + "brighttext" "1" + "dulltext" "0" + } + + "ImgIcon" + { + "ControlName" "ImagePanel" + "fieldName" "ImgIcon" + "xpos" "c-280" + "ypos" "315" + "zpos" "1" + "wide" "123" + "tall" "69" + "autoResize" "0" + "pinCorner" "0" + "visible" "1" + "enabled" "1" + "tabPosition" "0" + "scaleImage" "1" + } + + "BackButton" + { + "ControlName" "CNB_Button" + "fieldName" "BackButton" + "xpos" "c-264" + "ypos" "r23" + "wide" "117" + "tall" "27" + "zpos" "1" + "visible" "1" + "enabled" "1" + "tabPosition" "0" + "labelText" "#nb_back" + "command" "BackButton" + "textAlignment" "center" + "font" "DefaultMedium" + "fgcolor_override" "113 142 181 255" + } +} diff --git a/src/game/client/swarm/gameui/swarm/rd_challenge_selection.cpp b/src/game/client/swarm/gameui/swarm/rd_challenge_selection.cpp index 25506afa1..10eae0c96 100644 --- a/src/game/client/swarm/gameui/swarm/rd_challenge_selection.cpp +++ b/src/game/client/swarm/gameui/swarm/rd_challenge_selection.cpp @@ -1,4 +1,6 @@ #include "cbase.h" + +#include "asw_util_shared.h" #include "rd_challenge_selection.h" #include "rd_challenges_shared.h" #include @@ -6,6 +8,7 @@ #include #include "nb_header_footer.h" #include "nb_button.h" +#include "strtools.h" #include "vfooterpanel.h" #include "rd_workshop.h" #include "filesystem.h" @@ -14,6 +17,47 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +bool NameHasSearch( const char *szTitle, const char *szSearch ) +{ + if ( szSearch[0] == '\0' ) + { + return true; + } + + if ( szTitle[0] == '#' ) + { + wchar_t wszLocalized[RD_CHALLENGE_TITLE_LEN]; + char szLocalized[RD_CHALLENGE_TITLE_LEN]; + + TryLocalize( szTitle, wszLocalized, RD_CHALLENGE_TITLE_LEN ); + V_UnicodeToUTF8( wszLocalized, szLocalized, RD_CHALLENGE_TITLE_LEN ); + + return Q_stristr( szLocalized, szSearch ) != NULL; + } + + return Q_stristr( szTitle, szSearch ) != NULL; +} + +BaseModUI::ReactiveDropChallengeSelectionSearch::ReactiveDropChallengeSelectionSearch( vgui::Panel *parent, const char *panelName, ReactiveDropChallengeSelection *pSelection ) : BaseClass( parent, panelName ) +{ + SetMaximumCharCount( RD_CHALLENGE_TITLE_LEN ); + m_pSelection = pSelection; +} + +void BaseModUI::ReactiveDropChallengeSelectionSearch::OnKeyTyped( wchar_t unichar ) +{ + const int nBefore = GetTextLength(); + BaseClass::OnKeyTyped( unichar ); + const int nAfter = GetTextLength(); + + char szSearchText[RD_CHALLENGE_TITLE_LEN]; + GetText( szSearchText, RD_CHALLENGE_TITLE_LEN ); + + const bool bIncrSearch = nBefore != 0 && nAfter > nBefore; + + m_pSelection->PopulateChallenges( szSearchText, bIncrSearch ); +} + BaseModUI::ReactiveDropChallengeSelectionListItem::ReactiveDropChallengeSelectionListItem( vgui::Panel *parent, const char *panelName ) : BaseClass( parent, panelName ) { SetProportional( true ); @@ -219,6 +263,13 @@ const CReactiveDropWorkshop::WorkshopItem_t &BaseModUI::ReactiveDropChallengeSel BaseModUI::ReactiveDropChallengeSelection::ReactiveDropChallengeSelection( vgui::Panel *parent, const char *panelName, bool bDeathmatch ) : BaseClass( parent, panelName ) { SetProportional( true ); + SetMouseInputEnabled( true ); + SetKeyBoardInputEnabled( true ); + + if ( engine->IsConnected() ) + { + MakePopup( false ); + } m_pHeaderFooter = new CNB_Header_Footer( this, "HeaderFooter" ); m_pHeaderFooter->SetTitle( "" ); @@ -237,6 +288,8 @@ BaseModUI::ReactiveDropChallengeSelection::ReactiveDropChallengeSelection( vgui: m_lblDescription = new vgui::Label( this, "LblDescription", "" ); m_lblAuthor = new vgui::Label( this, "LblAuthor", "" ); + m_pTxtSearch = new ReactiveDropChallengeSelectionSearch( this, "TxtSearch", this ); + m_bIgnoreSelectionChange = false; m_bDeathmatch = bDeathmatch; @@ -309,7 +362,7 @@ void BaseModUI::ReactiveDropChallengeSelection::OnMessage( const KeyValues *para } } -void BaseModUI::ReactiveDropChallengeSelection::PopulateChallenges() +void BaseModUI::ReactiveDropChallengeSelection::PopulateChallenges( const char* szSearch, bool bIncrSearch ) { m_gplChallenges->RemoveAllPanelItems(); ReactiveDropChallengeSelectionListItem *pDisabled = m_gplChallenges->AddPanelItem( "ReactiveDropChallengeSelectionListItem" ); @@ -319,16 +372,55 @@ void BaseModUI::ReactiveDropChallengeSelection::PopulateChallenges() Assert( g_ReactiveDropWorkshop.m_bStartingUp == false ); g_ReactiveDropWorkshop.m_bStartingUp = true; - int iCount = ReactiveDropChallenges::Count(); - for ( int i = 0; i < iCount; i++ ) + const char* szChallenges[RD_MAX_CHALLENGES]; + int nChallenges = 0; + + // Find valid challenges + if ( bIncrSearch ) { - const RD_Challenge_t *pChallenge = ReactiveDropChallenges::GetSummary( i ); - if ( m_bDeathmatch ? pChallenge->AllowDeathmatch : pChallenge->AllowCoop ) + for ( int i = 0; i < m_nChallengesPrev; i++ ) { - ReactiveDropChallengeSelectionListItem *pItem = m_gplChallenges->AddPanelItem( "ReactiveDropChallengeSelectionListItem" ); - pItem->PopulateChallenge( ReactiveDropChallenges::Name( i ) ); - GetControllerFocus()->AddToFocusList( pItem, true, true ); + const char *pChallengeName = m_szChallengesPrev[i]; + const char *szTitle = ReactiveDropChallenges::DisplayName(pChallengeName); + + if ( NameHasSearch(szTitle, szSearch) ) + { + szChallenges[nChallenges] = pChallengeName; + m_szChallengesPrev[nChallenges] = pChallengeName; + nChallenges++; + } } + m_nChallengesPrev = nChallenges; + } + else + { + const int nRDChallenges = ReactiveDropChallenges::Count(); + Assert( nRDChallenges <= RD_MAX_CHALLENGES ); + for ( int i = 0; i < nRDChallenges; i++ ) + { + const RD_Challenge_t *pChallenge = ReactiveDropChallenges::GetSummary( i ); + if ( m_bDeathmatch ? pChallenge->AllowDeathmatch : pChallenge->AllowCoop ) + { + const char *pChallengeName = ReactiveDropChallenges::Name( i ); + const char *szTitle = pChallenge->Title; + + if ( !szSearch || NameHasSearch(szTitle, szSearch) ) + { + szChallenges[nChallenges] = pChallengeName; + m_szChallengesPrev[nChallenges] = pChallengeName; + nChallenges++; + } + } + } + m_nChallengesPrev = nChallenges; + } + + // Add valid challenges to the panel list + for ( int j = 0; j < nChallenges; j++ ) + { + ReactiveDropChallengeSelectionListItem *pItem = m_gplChallenges->AddPanelItem( "ReactiveDropChallengeSelectionListItem" ); + pItem->PopulateChallenge( szChallenges[j] ); + GetControllerFocus()->AddToFocusList( pItem, true, true ); } g_ReactiveDropWorkshop.m_bStartingUp = false; diff --git a/src/game/client/swarm/gameui/swarm/rd_challenge_selection.h b/src/game/client/swarm/gameui/swarm/rd_challenge_selection.h index 88f7ba6fc..fd09480d6 100644 --- a/src/game/client/swarm/gameui/swarm/rd_challenge_selection.h +++ b/src/game/client/swarm/gameui/swarm/rd_challenge_selection.h @@ -4,6 +4,8 @@ #include "basemodui.h" #include "vgenericpanellist.h" +#include "vgui_controls/TextEntry.h" +#include "rd_challenges_shared.h" #include "rd_workshop.h" class CNB_Header_Footer; @@ -11,6 +13,18 @@ class CNB_Button; namespace BaseModUI { + class ReactiveDropChallengeSelectionSearch : public vgui::TextEntry + { + DECLARE_CLASS_SIMPLE( ReactiveDropChallengeSelectionSearch, vgui::TextEntry ); + public: + ReactiveDropChallengeSelectionSearch( vgui::Panel *parent, const char *panelName, ReactiveDropChallengeSelection *pSelection ); + + virtual void OnKeyTyped( wchar_t unichar ); + + protected: + ReactiveDropChallengeSelection *m_pSelection; + }; + class ReactiveDropChallengeSelectionListItem : public vgui::EditablePanel, IGenericPanelListItem { DECLARE_CLASS_SIMPLE( ReactiveDropChallengeSelectionListItem, vgui::EditablePanel ); @@ -61,7 +75,7 @@ namespace BaseModUI virtual void OnMessage( const KeyValues *params, vgui::VPANEL ifromPanel ); void UpdateFooter(); - void PopulateChallenges(); + void PopulateChallenges( const char* szSearch = NULL, bool bIncrSearch = false ); void SetDetailsForChallenge( ReactiveDropChallengeSelectionListItem *pChallenge ); CNB_Header_Footer *m_pHeaderFooter; @@ -73,8 +87,14 @@ namespace BaseModUI vgui::Label *m_lblDescription; vgui::Label *m_lblAuthor; + ReactiveDropChallengeSelectionSearch *m_pTxtSearch; + bool m_bIgnoreSelectionChange; bool m_bDeathmatch; + + private: + const char* m_szChallengesPrev[RD_MAX_CHALLENGES]; + int m_nChallengesPrev = 0; }; } diff --git a/src/game/shared/swarm/rd_challenges_shared.h b/src/game/shared/swarm/rd_challenges_shared.h index a678928ee..3946f010a 100644 --- a/src/game/shared/swarm/rd_challenges_shared.h +++ b/src/game/shared/swarm/rd_challenges_shared.h @@ -5,6 +5,7 @@ class KeyValues; #define RD_MAX_CHALLENGES 1024 +#define RD_CHALLENGE_TITLE_LEN 255 #pragma pack(push, 1) struct RD_Challenge_t @@ -19,7 +20,7 @@ struct RD_Challenge_t bool RequiredOnClient : 1; bool _Reserved1 : 1; // Title must be last in this struct - char Title[255]; + char Title[RD_CHALLENGE_TITLE_LEN]; }; #pragma pack(pop)