/* * CPreGame.h, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */ #pragma once #include "../lib/StartInfo.h" #include "../lib/FunctionList.h" #include "../lib/mapping/CMapInfo.h" #include "../lib/rmg/CMapGenerator.h" #include "windows/CWindowObject.h" class CMapInfo; class CMusicHandler; class CMapHeader; class CCampaignHeader; class CTextInput; class CCampaign; class CGStatusBar; class CTextBox; class CCampaignState; class CConnection; class JsonNode; class CMapGenOptions; class CRandomMapTab; struct CPackForSelectionScreen; struct PlayerInfo; class CMultiLineLabel; class CToggleButton; class CToggleGroup; class CTabbedInt; class IImage; class CAnimation; class CAnimImage; class CButton; class CLabel; class CSlider; namespace boost{ class thread; class recursive_mutex;} enum ESortBy{_playerAm, _size, _format, _name, _viccon, _loscon, _numOfMaps, _fileName}; //_numOfMaps is for campaigns /// Class which handles map sorting by different criteria class mapSorter { public: ESortBy sortBy; bool operator()(const CMapInfo *aaa, const CMapInfo *bbb); mapSorter(ESortBy es):sortBy(es){}; }; /// The main menu screens listed in the EState enum class CMenuScreen : public CIntObject { const JsonNode& config; CTabbedInt *tabs; CPicture * background; std::vector images; CIntObject *createTab(size_t index); public: std::vector menuNameToEntry; enum EState { //where are we? mainMenu, newGame, loadGame, campaignMain, saveGame, scenarioInfo, campaignList }; enum EGameMode { SINGLE_PLAYER = 0, MULTI_HOT_SEAT, MULTI_NETWORK_HOST, MULTI_NETWORK_GUEST, SINGLE_CAMPAIGN }; CMenuScreen(const JsonNode& configNode); void showAll(SDL_Surface * to) override; void show(SDL_Surface * to) override; void activate() override; void deactivate() override; void switchToTab(size_t index); }; class CMenuEntry : public CIntObject { std::vector images; std::vector buttons; CButton* createButton(CMenuScreen* parent, const JsonNode& button); public: CMenuEntry(CMenuScreen* parent, const JsonNode &config); }; class CreditsScreen : public CIntObject { int positionCounter; CMultiLineLabel* credits; public: CreditsScreen(); void show(SDL_Surface * to) override; void clickLeft(tribool down, bool previousState) override; void clickRight(tribool down, bool previousState) override; }; /// Implementation of the chat box class CChatBox : public CIntObject { public: CTextBox *chatHistory; CTextInput *inputBox; CChatBox(const Rect &rect); void keyPressed(const SDL_KeyboardEvent & key) override; void addNewMessage(const std::string &text); }; class InfoCard : public CIntObject { CAnimImage * victory, * loss, *sizes; std::shared_ptr sFlags; public: CPicture *bg; bool network; bool chatOn; //if chat is shown, then description is hidden CTextBox *mapDescription; CChatBox *chat; CPicture *playerListBg; CToggleGroup *difficulty; void changeSelection(const CMapInfo *to); void showAll(SDL_Surface * to) override; void clickRight(tribool down, bool previousState) override; void showTeamsPopup(); void toggleChat(); void setChat(bool activateChat); InfoCard(bool Network = false); ~InfoCard(); }; /// The selection tab which is shown at the map selection screen class SelectionTab : public CIntObject { private: std::shared_ptr formatIcons; void parseMaps(const std::unordered_set &files); void parseGames(const std::unordered_set &files, CMenuScreen::EGameMode gameMode); std::unordered_set getFiles(std::string dirURI, int resType); void parseCampaigns(const std::unordered_set & files ); CMenuScreen::EState tabType; public: int positions; //how many entries (games/maps) can be shown CPicture *bg; //general bg image CSlider *slider; std::vector allItems; std::vector curItems; size_t selectionPos; std::function onSelect; ESortBy sortingBy; ESortBy generalSortingBy; bool ascending; CTextInput *txt; void filter(int size, bool selectFirst = false); //0 - all void select(int position); //position: <0 - positions> position on the screen void selectAbs(int position); //position: absolute position in curItems vector int getPosition(int x, int y); //convert mouse coords to entry position; -1 means none void sliderMove(int slidPos); void sortBy(int criteria); void sort(); void printMaps(SDL_Surface *to); int getLine(); void selectFName(std::string fname); const CMapInfo * getSelectedMapInfo() const; void showAll(SDL_Surface * to) override; void clickLeft(tribool down, bool previousState) override; void keyPressed(const SDL_KeyboardEvent & key) override; void onDoubleClick() override; SelectionTab(CMenuScreen::EState Type, const std::function &OnSelect, CMenuScreen::EGameMode GameMode = CMenuScreen::SINGLE_PLAYER); ~SelectionTab(); }; /// The options tab which is shown at the map selection phase. class OptionsTab : public CIntObject { CPicture *bg; public: enum SelType {TOWN, HERO, BONUS}; struct CPlayerSettingsHelper { const PlayerSettings & settings; const SelType type; CPlayerSettingsHelper(const PlayerSettings & settings, SelType type): settings(settings), type(type) {} /// visible image settings size_t getImageIndex(); std::string getImageName(); std::string getName(); /// name visible in options dialog std::string getTitle(); /// title in popup box std::string getSubtitle(); /// popup box subtitle std::string getDescription();/// popup box description, not always present }; class CPregameTooltipBox : public CWindowObject, public CPlayerSettingsHelper { void genHeader(); void genTownWindow(); void genHeroWindow(); void genBonusWindow(); public: CPregameTooltipBox(CPlayerSettingsHelper & helper); }; struct SelectedBox : public CIntObject, public CPlayerSettingsHelper //img with current town/hero/bonus { CAnimImage * image; CLabel *subtitle; SelectedBox(Point position, PlayerSettings & settings, SelType type); void clickRight(tribool down, bool previousState) override; void update(); }; struct PlayerOptionsEntry : public CIntObject { PlayerInfo π PlayerSettings &s; CPicture *bg; CButton *btns[6]; //left and right for town, hero, bonus CButton *flag; SelectedBox *town; SelectedBox *hero; SelectedBox *bonus; enum {HUMAN_OR_CPU, HUMAN, CPU} whoCanPlay; PlayerOptionsEntry(OptionsTab *owner, PlayerSettings &S); void selectButtons(); //hides unavailable buttons void showAll(SDL_Surface * to) override; void update(); }; CSlider *turnDuration; std::set usedHeroes; struct PlayerToRestore { PlayerColor color; int id; void reset() { id = -1; color = PlayerColor::CANNOT_DETERMINE; } PlayerToRestore(){ reset(); } } playerToRestore; std::map entries; //indexed by color void nextCastle(PlayerColor player, int dir); //dir == -1 or +1 void nextHero(PlayerColor player, int dir); //dir == -1 or +1 void nextBonus(PlayerColor player, int dir); //dir == -1 or +1 void setTurnLength(int npos); void flagPressed(PlayerColor player); void recreate(); OptionsTab(); ~OptionsTab(); void showAll(SDL_Surface * to) override; int nextAllowedHero(PlayerColor player, int min, int max, int incl, int dir ); bool canUseThisHero(PlayerColor player, int ID ); }; /// The random map tab shows options for generating a random map. class CRandomMapTab : public CIntObject { public: CRandomMapTab(); void showAll(SDL_Surface * to) override; void updateMapInfo(); CFunctionList & getMapInfoChanged(); const CMapInfo * getMapInfo() const; const CMapGenOptions & getMapGenOptions() const; void setMapGenOptions(std::shared_ptr opts); private: void addButtonsToGroup(CToggleGroup * group, const std::vector & defs, int startIndex, int endIndex, int btnWidth, int helpStartIndex) const; void addButtonsWithRandToGroup(CToggleGroup * group, const std::vector & defs, int startIndex, int endIndex, int btnWidth, int helpStartIndex, int helpRandIndex) const; void deactivateButtonsFrom(CToggleGroup * group, int startId); void validatePlayersCnt(int playersCnt); void validateCompOnlyPlayersCnt(int compOnlyPlayersCnt); std::vector getPossibleMapSizes(); CPicture * bg; CToggleButton * twoLevelsBtn; CToggleGroup * mapSizeBtnGroup, * playersCntGroup, * teamsCntGroup, * compOnlyPlayersCntGroup, * compOnlyTeamsCntGroup, * waterContentGroup, * monsterStrengthGroup; CButton * showRandMaps; CMapGenOptions mapGenOptions; std::unique_ptr mapInfo; CFunctionList mapInfoChanged; }; /// Interface for selecting a map. class ISelectionScreenInfo { public: CMenuScreen::EGameMode gameMode; CMenuScreen::EState screenType; //new/save/load#Game const CMapInfo *current; StartInfo sInfo; std::map playerNames; // id of player <-> player name; 0 is reserved as ID of AI "players" ISelectionScreenInfo(const std::map *Names = nullptr); virtual ~ISelectionScreenInfo(); virtual void update(){}; virtual void propagateOptions() {}; virtual void postRequest(ui8 what, ui8 dir) {}; virtual void postChatMessage(const std::string &txt){}; void setPlayer(PlayerSettings &pset, ui8 player); void updateStartInfo(std::string filename, StartInfo & sInfo, const std::unique_ptr & mapHeader); ui8 getIdOfFirstUnallocatedPlayer(); //returns 0 if none bool isGuest() const; bool isHost() const; }; /// The actual map selection screen which consists of the options and selection tab class CSelectionScreen : public CIntObject, public ISelectionScreenInfo { bool bordered; public: CPicture *bg; //general bg image InfoCard *card; OptionsTab *opt; CRandomMapTab * randMapTab; CButton *start, *back; SelectionTab *sel; CIntObject *curTab; boost::thread *serverHandlingThread; boost::recursive_mutex *mx; std::list upcomingPacks; //protected by mx CConnection *serv; //connection to server, used in MP mode bool ongoingClosing; ui8 myNameID; //used when networking - otherwise all player are "mine" CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EGameMode GameMode = CMenuScreen::SINGLE_PLAYER, const std::map * Names = nullptr, const std::string & Address = "", const ui16 Port = 0); ~CSelectionScreen(); void toggleTab(CIntObject *tab); void changeSelection(const CMapInfo *to); void startCampaign(); void startScenario(); void difficultyChange(int to); void handleConnection(); void processPacks(); void setSInfo(const StartInfo &si); void update() override; void propagateOptions() override; void postRequest(ui8 what, ui8 dir) override; void postChatMessage(const std::string &txt) override; void propagateNames(); void showAll(SDL_Surface *to) override; }; /// Save game screen class CSavingScreen : public CSelectionScreen { public: const CMapInfo *ourGame; CSavingScreen(bool hotseat = false); ~CSavingScreen(); }; /// Scenario information screen shown during the game (thus not really a "pre-game" but fits here anyway) class CScenarioInfo : public CIntObject, public ISelectionScreenInfo { public: CButton *back; InfoCard *card; OptionsTab *opt; CScenarioInfo(const CMapHeader *mapHeader, const StartInfo *startInfo); ~CScenarioInfo(); }; /// Multiplayer mode class CMultiMode : public CIntObject { public: CPicture *bg; CTextInput *txt; CButton *btns[7]; //0 - hotseat, 6 - cancel CGStatusBar *bar; CMultiMode(); void openHotseat(); void hostTCP(); void joinTCP(); }; /// Hot seat player window class CHotSeatPlayers : public CIntObject { CPicture *bg; CTextBox *title; CTextInput* txt[8]; CButton *ok, *cancel; CGStatusBar *bar; void onChange(std::string newText); void enterSelectionScreen(); public: CHotSeatPlayers(const std::string &firstPlayer); }; class CPrologEpilogVideo : public CWindowObject { CCampaignScenario::SScenarioPrologEpilog spe; int positionCounter; int voiceSoundHandle; std::function exitCb; CMultiLineLabel * text; public: CPrologEpilogVideo(CCampaignScenario::SScenarioPrologEpilog _spe, std::function callback); void clickLeft(tribool down, bool previousState) override; void show(SDL_Surface * to) override; }; /// Campaign screen where you can choose one out of three starting bonuses class CBonusSelection : public CIntObject { public: CBonusSelection(const std::string & campaignFName); CBonusSelection(std::shared_ptr _ourCampaign); ~CBonusSelection(); void showAll(SDL_Surface * to) override; void show(SDL_Surface * to) override; private: struct SCampPositions { std::string campPrefix; int colorSuffixLength; struct SRegionDesc { std::string infix; int xpos, ypos; }; std::vector regions; }; class CRegion : public CIntObject { CBonusSelection * owner; SDL_Surface* graphics[3]; //[0] - not selected, [1] - selected, [2] - striped bool accessible; //false if region should be striped bool selectable; //true if region should be selectable int myNumber; //number of region public: std::string rclickText; CRegion(CBonusSelection * _owner, bool _accessible, bool _selectable, int _myNumber); ~CRegion(); void clickLeft(tribool down, bool previousState) override; void clickRight(tribool down, bool previousState) override; void show(SDL_Surface * to) override; }; void init(); void loadPositionsOfGraphics(); void updateStartButtonState(int selected = -1); //-1 -- no bonus is selected void updateBonusSelection(); void updateCampaignState(); // Event handlers void goBack(); void startMap(); void restartMap(); void selectMap(int mapNr, bool initialSelect); void selectBonus(int id); void increaseDifficulty(); void decreaseDifficulty(); // GUI components SDL_Surface * background; CButton * startB, * restartB, * backB; CTextBox * campaignDescription, * mapDescription; std::vector campDescriptions; std::vector regions; CRegion * highlightedRegion; CToggleGroup * bonuses; std::array diffPics; //pictures of difficulties, user-selectable (or not if campaign locks this) CButton * diffLb, * diffRb; //buttons for changing difficulty CAnimImage * sizes;//icons of map sizes std::shared_ptr sFlags; // Data std::shared_ptr ourCampaign; int selectedMap; boost::optional selectedBonus; StartInfo startInfo; std::unique_ptr ourHeader; }; /// Campaign selection screen class CCampaignScreen : public CIntObject { public: enum CampaignStatus {DEFAULT = 0, ENABLED, DISABLED, COMPLETED}; // the status of the campaign private: /// A button which plays a video when you move the mouse cursor over it class CCampaignButton : public CIntObject { private: CLabel *hoverLabel; CampaignStatus status; std::string campFile; // the filename/resourcename of the campaign std::string video; // the resource name of the video std::string hoverText; void clickLeft(tribool down, bool previousState) override; void hover(bool on) override; public: CCampaignButton(const JsonNode &config ); void show(SDL_Surface * to) override; }; std::vector campButtons; std::vector images; CButton* createExitButton(const JsonNode& button); public: enum CampaignSet {ROE, AB, SOD, WOG}; CCampaignScreen(const JsonNode &config); void showAll(SDL_Surface *to) override; }; /// Manages the configuration of pregame GUI elements like campaign screen, main menu, loading screen,... class CGPreGameConfig { public: static CGPreGameConfig & get(); const JsonNode & getConfig() const; const JsonNode & getCampaigns() const; private: CGPreGameConfig(); const JsonNode campaignSets; const JsonNode config; }; /// Handles background screen, loads graphics for victory/loss condition and random town or hero selection class CGPreGame : public CIntObject, public IUpdateable { void loadGraphics(); void disposeGraphics(); CGPreGame(); //Use createIfNotPresent public: CMenuScreen * menu; std::shared_ptr victoryIcons, lossIcons; ~CGPreGame(); void update() override; void openSel(CMenuScreen::EState type, CMenuScreen::EGameMode gameMode = CMenuScreen::SINGLE_PLAYER); void openCampaignScreen(std::string name); static CGPreGame * create(); void removeFromGui(); static void showLoadingScreen(std::function loader); }; class CLoadingScreen : public CWindowObject { boost::thread loadingThread; std::string getBackground(); public: CLoadingScreen(std::function loader); ~CLoadingScreen(); void showAll(SDL_Surface *to) override; }; /// Simple window to enter the server's address. class CSimpleJoinScreen : public CIntObject { CPicture * bg; CTextBox * title; CButton * ok, * cancel; CGStatusBar * bar; CTextInput * address; CTextInput * port; void enterSelectionScreen(CMenuScreen::EGameMode mode); void onChange(const std::string & newText); public: CSimpleJoinScreen(CMenuScreen::EGameMode mode); }; extern ISelectionScreenInfo *SEL; extern CGPreGame *CGP;