Files
vcmi/client/mainmenu/CMainMenu.cpp
T
George King 26eac7d82b Improved Main menu
1) Added option to use custom logo and sublogo on Main menu, Scenario selection and Loading screen
2) Added option to use custom loading bar frame on Loading screen
2025-01-31 14:20:10 +01:00

825 lines
27 KiB
C++

/*
* CMainMenu.cpp, 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
*
*/
#include "StdInc.h"
#include "CMainMenu.h"
#include "CCampaignScreen.h"
#include "CreditsScreen.h"
#include "CHighScoreScreen.h"
#include "../lobby/CBonusSelection.h"
#include "../lobby/CSelectionBase.h"
#include "../lobby/CLobbyScreen.h"
#include "../media/IMusicPlayer.h"
#include "../media/IVideoPlayer.h"
#include "../gui/CursorHandler.h"
#include "../windows/GUIClasses.h"
#include "../gui/CGuiHandler.h"
#include "../gui/ShortcutHandler.h"
#include "../gui/Shortcut.h"
#include "../gui/WindowHandler.h"
#include "../render/Canvas.h"
#include "../globalLobby/GlobalLobbyLoginWindow.h"
#include "../globalLobby/GlobalLobbyClient.h"
#include "../globalLobby/GlobalLobbyWindow.h"
#include "../widgets/CComponent.h"
#include "../widgets/Buttons.h"
#include "../widgets/CTextInput.h"
#include "../widgets/MiscWidgets.h"
#include "../widgets/ObjectLists.h"
#include "../widgets/TextControls.h"
#include "../widgets/VideoWidget.h"
#include "../windows/InfoWindows.h"
#include "../CServerHandler.h"
#include "../render/AssetGenerator.h"
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../Client.h"
#include "../CMT.h"
#include "../../CCallback.h"
#include "../../lib/texts/CGeneralTextHandler.h"
#include "../../lib/campaign/CampaignHandler.h"
#include "../../lib/filesystem/Filesystem.h"
#include "../../lib/filesystem/CCompressedStream.h"
#include "../../lib/mapping/CMapInfo.h"
#include "../../lib/modding/CModHandler.h"
#include "../../lib/VCMIDirs.h"
#include "../../lib/CStopWatch.h"
#include "../../lib/CThreadHelper.h"
#include "../../lib/CConfigHandler.h"
#include "../../lib/GameConstants.h"
#include "../../lib/CRandomGenerator.h"
std::shared_ptr<CMainMenu> CMM;
ISelectionScreenInfo * SEL = nullptr;
static void do_quit()
{
GH.dispatchMainThread([]()
{
handleQuit(false);
});
}
CMenuScreen::CMenuScreen(const JsonNode & configNode)
: CWindowObject(BORDERED), config(configNode)
{
OBJECT_CONSTRUCTION;
const auto& bgConfig = config["background"];
if (bgConfig.isVector() && !bgConfig.Vector().empty())
background = std::make_shared<CPicture>(ImagePath::fromJson(*RandomGeneratorUtil::nextItem(bgConfig.Vector(), CRandomGenerator::getDefault())));
if (bgConfig.isString())
background = std::make_shared<CPicture>(ImagePath::fromJson(bgConfig));
if(config["scalable"].Bool())
background->scaleTo(GH.screenDimensions());
pos = background->center();
const auto& logoConfig = config["logo"];
if (!logoConfig.isNull() && logoConfig["name"].isVector() && !logoConfig["name"].Vector().empty()) {
auto logoImage = std::make_shared<CPicture>(ImagePath::fromJson(*RandomGeneratorUtil::nextItem(logoConfig["name"].Vector(), CRandomGenerator::getDefault())), Point(logoConfig["x"].Integer(), logoConfig["y"].Integer()));
images.push_back(logoImage);
}
const auto& sublogoConfig = config["sublogo"];
if (!sublogoConfig.isNull() && sublogoConfig["name"].isVector() && !sublogoConfig["name"].Vector().empty()) {
auto logoImage = std::make_shared<CPicture>(ImagePath::fromJson(*RandomGeneratorUtil::nextItem(sublogoConfig["name"].Vector(), CRandomGenerator::getDefault())), Point(sublogoConfig["x"].Integer(), sublogoConfig["y"].Integer()));
images.push_back(logoImage);
}
if(!config["video"].isNull())
{
Point videoPosition(config["video"]["x"].Integer(), config["video"]["y"].Integer());
videoPlayer = std::make_shared<VideoWidget>(videoPosition, VideoPath::fromJson(config["video"]["name"]), false);
}
for(const JsonNode & node : config["items"].Vector())
menuNameToEntry.push_back(node["name"].String());
for(const JsonNode & node : config["images"].Vector())
images.push_back(CMainMenu::createPicture(node));
//Hardcoded entry
menuNameToEntry.push_back("credits");
tabs = std::make_shared<CTabbedInt>(std::bind(&CMenuScreen::createTab, this, _1));
if(config["video"].isNull())
tabs->setRedrawParent(true);
}
std::shared_ptr<CIntObject> CMenuScreen::createTab(size_t index)
{
if(config["items"].Vector().size() == index)
return std::make_shared<CreditsScreen>(this->pos);
else
return std::make_shared<CMenuEntry>(this, config["items"].Vector()[index]);
}
void CMenuScreen::show(Canvas & to)
{
// TODO: avoid excessive redraws
CIntObject::showAll(to);
}
void CMenuScreen::activate()
{
CIntObject::activate();
}
void CMenuScreen::switchToTab(size_t index)
{
tabs->setActive(index);
}
void CMenuScreen::switchToTab(std::string name)
{
switchToTab(vstd::find_pos(menuNameToEntry, name));
}
size_t CMenuScreen::getActiveTab() const
{
return tabs->getActive();
}
//function for std::string -> std::function conversion for main menu
static std::function<void()> genCommand(CMenuScreen * menu, std::vector<std::string> menuType, const std::string & string)
{
static const std::vector<std::string> commandType = {"to", "campaigns", "start", "load", "exit", "highscores"};
static const std::vector<std::string> gameType = {"single", "multi", "campaign", "tutorial"};
std::list<std::string> commands;
boost::split(commands, string, boost::is_any_of("\t "));
if(!commands.empty())
{
size_t index = std::find(commandType.begin(), commandType.end(), commands.front()) - commandType.begin();
commands.pop_front();
if(index > 3 || !commands.empty())
{
switch(index)
{
case 0: //to - switch to another tab, if such tab exists
{
size_t index2 = std::find(menuType.begin(), menuType.end(), commands.front()) - menuType.begin();
if(index2 != menuType.size())
return std::bind((void(CMenuScreen::*)(size_t))&CMenuScreen::switchToTab, menu, index2);
break;
}
case 1: //open campaign selection window
{
return std::bind(&CMainMenu::openCampaignScreen, CMM, commands.front());
break;
}
case 2: //start
{
switch(std::find(gameType.begin(), gameType.end(), commands.front()) - gameType.begin())
{
case 0:
return []() { CMainMenu::openLobby(ESelectionScreen::newGame, true, {}, ELoadMode::NONE); };
case 1:
return []() { GH.windows().createAndPushWindow<CMultiMode>(ESelectionScreen::newGame); };
case 2:
return []() { CMainMenu::openLobby(ESelectionScreen::campaignList, true, {}, ELoadMode::NONE); };
case 3:
return []() { CMainMenu::startTutorial(); };
}
break;
}
case 3: //load
{
switch(std::find(gameType.begin(), gameType.end(), commands.front()) - gameType.begin())
{
case 0:
return []() { CMainMenu::openLobby(ESelectionScreen::loadGame, true, {}, ELoadMode::SINGLE); };
case 1:
return []() { GH.windows().createAndPushWindow<CMultiMode>(ESelectionScreen::loadGame); };
case 2:
return []() { CMainMenu::openLobby(ESelectionScreen::loadGame, true, {}, ELoadMode::CAMPAIGN); };
case 3:
return []() { CMainMenu::openLobby(ESelectionScreen::loadGame, true, {}, ELoadMode::TUTORIAL); };
}
}
break;
case 4: //exit
{
return []() { CInfoWindow::showYesNoDialog(CGI->generaltexth->allTexts[69], std::vector<std::shared_ptr<CComponent>>(), do_quit, 0, PlayerColor(1)); };
}
break;
case 5: //highscores
{
return []() { CMainMenu::openHighScoreScreen(); };
}
}
}
}
logGlobal->error("Failed to parse command: %s", string);
return std::function<void()>();
}
std::shared_ptr<CButton> CMenuEntry::createButton(CMenuScreen * parent, const JsonNode & button)
{
std::function<void()> command = genCommand(parent, parent->menuNameToEntry, button["command"].String());
std::pair<std::string, std::string> help;
if(!button["help"].isNull() && button["help"].Float() > 0)
help = CGI->generaltexth->zelp[(size_t)button["help"].Float()];
int posx = static_cast<int>(button["x"].Float());
if(posx < 0)
posx = pos.w + posx;
int posy = static_cast<int>(button["y"].Float());
if(posy < 0)
posy = pos.h + posy;
EShortcut shortcut = GH.shortcuts().findShortcut(button["shortcut"].String());
if (shortcut == EShortcut::NONE && !button["shortcut"].String().empty())
{
logGlobal->warn("Unknown shortcut '%s' found when loading main menu config!", button["shortcut"].String());
}
auto result = std::make_shared<CButton>(Point(posx, posy), AnimationPath::fromJson(button["name"]), help, command, shortcut);
if (button["center"].Bool())
result->moveBy(Point(-result->pos.w/2, -result->pos.h/2));
return result;
}
CMenuEntry::CMenuEntry(CMenuScreen * parent, const JsonNode & config)
{
OBJECT_CONSTRUCTION;
setRedrawParent(true);
pos = parent->pos;
for(const JsonNode & node : config["images"].Vector())
images.push_back(CMainMenu::createPicture(node));
for (const JsonNode& node : config["buttons"].Vector()) {
auto tokens = node["command"].String().find(' ');
std::pair<std::string, std::string> commandParts = {
node["command"].String().substr(0, tokens),
(tokens == std::string::npos) ? "" : node["command"].String().substr(tokens + 1)
};
if (commandParts.first == "campaigns") {
const auto& campaign = CMainMenuConfig::get().getCampaigns()[commandParts.second];
if (!campaign.isStruct()) {
logGlobal->warn("Campaign set %s not found", commandParts.second);
continue;
}
bool fileExists = false;
for (const auto& item : campaign["items"].Vector()) {
std::string filename = item["file"].String();
if (CResourceHandler::get()->existsResource(ResourcePath(filename + ".h3c"))) {
fileExists = true;
break;
}
}
if (!fileExists) {
logGlobal->warn("No valid files found for campaign set %s", commandParts.second);
continue;
}
}
buttons.push_back(createButton(parent, node));
buttons.back()->setHoverable(true);
buttons.back()->setRedrawParent(true);
}
}
CMainMenuConfig::CMainMenuConfig()
: campaignSets(JsonPath::builtin("config/campaignSets.json"))
, config(JsonPath::builtin("config/mainmenu.json"))
{
if (!config["scenario-selection"].isStruct())
handleFatalError("The main menu configuration file mainmenu.json is invalid or corrupted. Please check the file for errors, verify your mod setup, or reinstall VCMI to resolve the issue.", false);
}
const CMainMenuConfig & CMainMenuConfig::get()
{
static const CMainMenuConfig config;
return config;
}
const JsonNode & CMainMenuConfig::getConfig() const
{
return config;
}
const JsonNode & CMainMenuConfig::getCampaigns() const
{
return campaignSets;
}
CMainMenu::CMainMenu()
{
pos.w = GH.screenDimensions().x;
pos.h = GH.screenDimensions().y;
menu = std::make_shared<CMenuScreen>(CMainMenuConfig::get().getConfig()["window"]);
OBJECT_CONSTRUCTION;
backgroundAroundMenu = std::make_shared<CFilledTexture>(ImagePath::builtin("DIBOXBCK"), pos);
}
CMainMenu::~CMainMenu()
{
if(GH.curInt == this)
GH.curInt = nullptr;
}
void CMainMenu::playIntroVideos()
{
auto playVideo = [](std::string video, bool rim, float scaleFactor, std::function<void(bool)> cb){
if(CCS->videoh->open(VideoPath::builtin(video), scaleFactor))
GH.windows().createAndPushWindow<VideoWindow>(VideoPath::builtin(video), rim ? ImagePath::builtin("INTRORIM") : ImagePath::builtin(""), true, scaleFactor, [cb](bool skipped){ cb(skipped); });
else
cb(true);
};
playVideo("3DOLOGO.SMK", false, 1.25, [playVideo, this](bool skipped){
if(!skipped)
playVideo("NWCLOGO.SMK", false, 2, [playVideo, this](bool skipped){
if(!skipped)
playVideo("H3INTRO.SMK", true, 1, [this](bool skipped){
playMusic();
});
else
playMusic();
});
else
playMusic();
});
}
void CMainMenu::playMusic()
{
CCS->musich->playMusic(AudioPath::builtin("Music/MainMenu"), true, true);
}
void CMainMenu::activate()
{
// check if screen was resized while main menu was inactive - e.g. in gameplay mode
if (pos.dimensions() != GH.screenDimensions())
onScreenResize();
CIntObject::activate();
}
void CMainMenu::onScreenResize()
{
pos.w = GH.screenDimensions().x;
pos.h = GH.screenDimensions().y;
menu = nullptr;
menu = std::make_shared<CMenuScreen>(CMainMenuConfig::get().getConfig()["window"]);
backgroundAroundMenu->pos = pos;
}
void CMainMenu::update()
{
if(CMM != this->shared_from_this()) //don't update if you are not a main interface
return;
if(GH.windows().count() == 0)
{
GH.windows().pushWindow(CMM);
GH.windows().pushWindow(menu);
menu->switchToTab(menu->getActiveTab());
}
// Handles mouse and key input
GH.handleEvents();
GH.windows().simpleRedraw();
}
void CMainMenu::openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> & names, ELoadMode loadMode)
{
CSH->resetStateForLobby(screenType == ESelectionScreen::newGame ? EStartMode::NEW_GAME : EStartMode::LOAD_GAME, screenType, EServerMode::LOCAL, names);
CSH->loadMode = loadMode;
GH.windows().createAndPushWindow<CSimpleJoinScreen>(host);
}
void CMainMenu::openCampaignLobby(const std::string & campaignFileName, std::string campaignSet)
{
auto ourCampaign = CampaignHandler::getCampaign(campaignFileName);
ourCampaign->campaignSet = campaignSet;
openCampaignLobby(ourCampaign);
}
void CMainMenu::openCampaignLobby(std::shared_ptr<CampaignState> campaign)
{
CSH->resetStateForLobby(EStartMode::CAMPAIGN, ESelectionScreen::campaignList, EServerMode::LOCAL, {});
CSH->campaignStateToSend = campaign;
GH.windows().createAndPushWindow<CSimpleJoinScreen>();
}
void CMainMenu::openCampaignScreen(std::string name)
{
auto const & config = CMainMenuConfig::get().getCampaigns();
AssetGenerator::createCampaignBackground();
AssetGenerator::createChroniclesCampaignImages();
if(!vstd::contains(config.Struct(), name))
{
logGlobal->error("Unknown campaign set: %s", name);
return;
}
bool campaignsFound = true;
for (auto const & entry : config[name]["items"].Vector())
{
ResourcePath resourceID(entry["file"].String(), EResType::CAMPAIGN);
if(entry["optional"].Bool())
continue;
if(!CResourceHandler::get()->existsResource(resourceID))
campaignsFound = false;
}
if (!campaignsFound)
{
CInfoWindow::showInfoDialog(CGI->generaltexth->translate("vcmi.client.errors.missingCampaigns"), std::vector<std::shared_ptr<CComponent>>(), PlayerColor(1));
return;
}
GH.windows().createAndPushWindow<CCampaignScreen>(config, name);
}
void CMainMenu::startTutorial()
{
ResourcePath tutorialMap("Maps/Tutorial.tut", EResType::MAP);
if(!CResourceHandler::get()->existsResource(tutorialMap))
{
CInfoWindow::showInfoDialog(CGI->generaltexth->translate("core.genrltxt.742"), std::vector<std::shared_ptr<CComponent>>(), PlayerColor(1));
return;
}
auto mapInfo = std::make_shared<CMapInfo>();
mapInfo->mapInit(tutorialMap.getName());
CMainMenu::openLobby(ESelectionScreen::newGame, true, {}, ELoadMode::NONE);
CSH->startMapAfterConnection(mapInfo);
}
void CMainMenu::openHighScoreScreen()
{
GH.windows().createAndPushWindow<CHighScoreScreen>(CHighScoreScreen::HighScorePage::SCENARIO);
return;
}
std::shared_ptr<CMainMenu> CMainMenu::create()
{
if(!CMM)
CMM = std::shared_ptr<CMainMenu>(new CMainMenu());
return CMM;
}
std::shared_ptr<CPicture> CMainMenu::createPicture(const JsonNode & config)
{
return std::make_shared<CPicture>(ImagePath::fromJson(config["name"]), (int)config["x"].Float(), (int)config["y"].Float());
}
CMultiMode::CMultiMode(ESelectionScreen ScreenType)
: screenType(ScreenType)
{
OBJECT_CONSTRUCTION;
background = std::make_shared<CPicture>(ImagePath::builtin("MUPOPUP.bmp"));
pos = background->center(); //center, window has size of bg graphic
const auto& multiplayerConfig = CMainMenuConfig::get().getConfig()["multiplayer"];
if (multiplayerConfig.isVector() && !multiplayerConfig.Vector().empty())
picture = std::make_shared<CPicture>(ImagePath::fromJson(*RandomGeneratorUtil::nextItem(multiplayerConfig.Vector(), CRandomGenerator::getDefault())), 16, 77);
textTitle = std::make_shared<CTextBox>("", Rect(7, 18, 440, 50), 0, FONT_BIG, ETextAlignment::CENTER, Colors::WHITE);
textTitle->setText(CGI->generaltexth->zelp[263].second);
statusBar = CGStatusBar::create(std::make_shared<CPicture>(background->getSurface(), Rect(7, 465, 440, 18), 7, 465));
playerName = std::make_shared<CTextInput>(Rect(19, 436, 334, 16), background->getSurface());
playerName->setText(getPlayersNames()[0]);
playerName->setCallback(std::bind(&CMultiMode::onNameChange, this, _1));
buttonHotseat = std::make_shared<CButton>(Point(373, 78 + 57 * 0), AnimationPath::builtin("MUBHOT.DEF"), CGI->generaltexth->zelp[266], std::bind(&CMultiMode::hostTCP, this, EShortcut::MAIN_MENU_HOTSEAT), EShortcut::MAIN_MENU_HOTSEAT);
buttonLobby = std::make_shared<CButton>(Point(373, 78 + 57 * 1), AnimationPath::builtin("MUBONL.DEF"), CGI->generaltexth->zelp[265], std::bind(&CMultiMode::openLobby, this), EShortcut::MAIN_MENU_LOBBY);
buttonHost = std::make_shared<CButton>(Point(373, 78 + 57 * 3), AnimationPath::builtin("MUBHOST.DEF"), CButton::tooltip(CGI->generaltexth->translate("vcmi.mainMenu.hostTCP"), ""), std::bind(&CMultiMode::hostTCP, this, EShortcut::MAIN_MENU_HOST_GAME), EShortcut::MAIN_MENU_HOST_GAME);
buttonJoin = std::make_shared<CButton>(Point(373, 78 + 57 * 4), AnimationPath::builtin("MUBJOIN.DEF"), CButton::tooltip(CGI->generaltexth->translate("vcmi.mainMenu.joinTCP"), ""), std::bind(&CMultiMode::joinTCP, this, EShortcut::MAIN_MENU_JOIN_GAME), EShortcut::MAIN_MENU_JOIN_GAME);
buttonCancel = std::make_shared<CButton>(Point(373, 424), AnimationPath::builtin("MUBCANC.DEF"), CGI->generaltexth->zelp[288], [=](){ close();}, EShortcut::GLOBAL_CANCEL);
}
void CMultiMode::openLobby()
{
close();
CSH->getGlobalLobby().activateInterface();
}
void CMultiMode::hostTCP(EShortcut shortcut)
{
auto savedScreenType = screenType;
close();
GH.windows().createAndPushWindow<CMultiPlayers>(getPlayersNames(), savedScreenType, true, ELoadMode::MULTI, shortcut);
}
void CMultiMode::joinTCP(EShortcut shortcut)
{
auto savedScreenType = screenType;
close();
GH.windows().createAndPushWindow<CMultiPlayers>(getPlayersNames(), savedScreenType, false, ELoadMode::MULTI, shortcut);
}
std::vector<std::string> CMultiMode::getPlayersNames()
{
std::vector<std::string> playerNames;
std::string playerNameStr = settings["general"]["playerName"].String();
if (playerNameStr == "Player")
playerNameStr = CGI->generaltexth->translate("core.genrltxt.434");
playerNames.push_back(playerNameStr);
for (const auto & playerName : settings["general"]["multiPlayerNames"].Vector())
{
const std::string &nameStr = playerName.String();
if (!nameStr.empty())
{
playerNames.push_back(nameStr);
}
}
return playerNames;
}
void CMultiMode::onNameChange(std::string newText)
{
Settings name = settings.write["general"]["playerName"];
name->String() = newText;
}
CMultiPlayers::CMultiPlayers(const std::vector<std::string>& playerNames, ESelectionScreen ScreenType, bool Host, ELoadMode LoadMode, EShortcut shortcut)
: loadMode(LoadMode), screenType(ScreenType), host(Host)
{
OBJECT_CONSTRUCTION;
background = std::make_shared<CPicture>(ImagePath::builtin("MUHOTSEA.bmp"));
pos = background->center(); //center, window has size of bg graphic
std::string text;
switch (shortcut)
{
case EShortcut::MAIN_MENU_HOTSEAT:
text = CGI->generaltexth->allTexts[446];
boost::replace_all(text, "\t", "\n");
break;
case EShortcut::MAIN_MENU_HOST_GAME:
text = CGI->generaltexth->translate("vcmi.mainMenu.hostTCP");
break;
case EShortcut::MAIN_MENU_JOIN_GAME:
text = CGI->generaltexth->translate("vcmi.mainMenu.joinTCP");
break;
}
textTitle = std::make_shared<CTextBox>(text, Rect(25, 10, 315, 60), 0, FONT_BIG, ETextAlignment::CENTER, Colors::WHITE);
for(int i = 0; i < inputNames.size(); i++)
{
inputNames[i] = std::make_shared<CTextInput>(Rect(60, 85 + i * 30, 280, 16), background->getSurface());
inputNames[i]->setCallback(std::bind(&CMultiPlayers::onChange, this, _1));
}
buttonOk = std::make_shared<CButton>(Point(95, 338), AnimationPath::builtin("MUBCHCK.DEF"), CGI->generaltexth->zelp[560], std::bind(&CMultiPlayers::enterSelectionScreen, this), EShortcut::GLOBAL_ACCEPT);
buttonCancel = std::make_shared<CButton>(Point(205, 338), AnimationPath::builtin("MUBCANC.DEF"), CGI->generaltexth->zelp[561], [=](){ close();}, EShortcut::GLOBAL_CANCEL);
statusBar = CGStatusBar::create(std::make_shared<CPicture>(background->getSurface(), Rect(7, 381, 348, 18), 7, 381));
for(int i = 0; i < playerNames.size(); i++)
{
inputNames[i]->setText(playerNames[i]);
}
#ifndef VCMI_MOBILE
inputNames[0]->giveFocus();
#endif
}
void CMultiPlayers::onChange(std::string newText)
{
}
void CMultiPlayers::enterSelectionScreen()
{
std::vector<std::string> playerNames;
for(auto playerName : inputNames)
{
if (playerName->getText().length())
playerNames.push_back(playerName->getText());
}
Settings playerName = settings.write["general"]["playerName"];
Settings multiPlayerNames = settings.write["general"]["multiPlayerNames"];
multiPlayerNames->Vector().clear();
if (!playerNames.empty())
{
playerName->String() = playerNames.front();
for (auto playerNameIt = playerNames.begin()+1; playerNameIt != playerNames.end(); playerNameIt++)
{
multiPlayerNames->Vector().push_back(JsonNode(*playerNameIt));
}
}
else
{
// Without the check the saving crashes directly.
// When empty reset the player's name. This would translate to it being
// the default for the next run. But enables deleting players, by just
// deleting the names, otherwise some UI element should have been added.
playerName->clear();
}
CMainMenu::openLobby(screenType, host, playerNames, loadMode);
}
CSimpleJoinScreen::CSimpleJoinScreen(bool host)
{
OBJECT_CONSTRUCTION;
background = std::make_shared<CPicture>(ImagePath::builtin("MUDIALOG.bmp")); // address background
pos = background->center(); //center, window has size of bg graphic (x,y = 396,278 w=232 h=212)
textTitle = std::make_shared<CTextBox>("", Rect(20, 10, 205, 50), 0, FONT_BIG, ETextAlignment::CENTER, Colors::WHITE);
inputAddress = std::make_shared<CTextInput>(Rect(25, 68, 175, 16), background->getSurface());
inputPort = std::make_shared<CTextInput>(Rect(25, 115, 175, 16), background->getSurface());
buttonOk = std::make_shared<CButton>(Point(26, 142), AnimationPath::builtin("MUBCHCK.DEF"), CGI->generaltexth->zelp[560], std::bind(&CSimpleJoinScreen::connectToServer, this), EShortcut::GLOBAL_ACCEPT);
if(host && !settings["session"]["donotstartserver"].Bool())
{
textTitle->setText(CGI->generaltexth->translate("vcmi.mainMenu.serverConnecting"));
buttonOk->block(true);
startConnection();
}
else
{
textTitle->setText(CGI->generaltexth->translate("vcmi.mainMenu.serverAddressEnter"));
inputAddress->setCallback(std::bind(&CSimpleJoinScreen::onChange, this, _1));
inputPort->setCallback(std::bind(&CSimpleJoinScreen::onChange, this, _1));
inputPort->setFilterNumber(0, 65535);
inputAddress->giveFocus();
}
inputAddress->setText(host ? CSH->getLocalHostname() : CSH->getRemoteHostname());
inputPort->setText(std::to_string(host ? CSH->getLocalPort() : CSH->getRemotePort()));
buttonOk->block(inputAddress->getText().empty() || inputPort->getText().empty());
buttonCancel = std::make_shared<CButton>(Point(142, 142), AnimationPath::builtin("MUBCANC.DEF"), CGI->generaltexth->zelp[561], std::bind(&CSimpleJoinScreen::leaveScreen, this), EShortcut::GLOBAL_CANCEL);
statusBar = CGStatusBar::create(std::make_shared<CPicture>(background->getSurface(), Rect(7, 186, 218, 18), 7, 186));
}
void CSimpleJoinScreen::connectToServer()
{
textTitle->setText(CGI->generaltexth->translate("vcmi.mainMenu.serverConnecting"));
buttonOk->block(true);
GH.stopTextInput();
startConnection(inputAddress->getText(), boost::lexical_cast<ui16>(inputPort->getText()));
}
void CSimpleJoinScreen::leaveScreen()
{
textTitle->setText(CGI->generaltexth->translate("vcmi.mainMenu.serverClosing"));
CSH->setState(EClientState::CONNECTION_CANCELLED);
}
void CSimpleJoinScreen::onChange(const std::string & newText)
{
buttonOk->block(inputAddress->getText().empty() || inputPort->getText().empty());
}
void CSimpleJoinScreen::startConnection(const std::string & addr, ui16 port)
{
if(addr.empty())
CSH->startLocalServerAndConnect(false);
else
CSH->connectToServer(addr, port);
}
CLoadingScreen::CLoadingScreen()
: CLoadingScreen(getBackground())
{
}
CLoadingScreen::CLoadingScreen(ImagePath background)
: CWindowObject(BORDERED, background)
{
OBJECT_CONSTRUCTION;
addUsedEvents(TIME);
CCS->musich->stopMusic(5000);
const auto& conf = CMainMenuConfig::get().getConfig()["loading"];
// Load background
const auto& backgroundConfig = conf["background"];
if (backgroundConfig.isVector() && !backgroundConfig.Vector().empty())
this->background = std::make_shared<CPicture>(ImagePath::fromJson(*RandomGeneratorUtil::nextItem(backgroundConfig.Vector(), CRandomGenerator::getDefault())));
// Load logo
const auto& logoConfig = conf["logo"];
if (!logoConfig.isNull() && logoConfig["name"].isVector() && !logoConfig["name"].Vector().empty()) {
this->logo = std::make_shared<CPicture>(
ImagePath::fromJson(*RandomGeneratorUtil::nextItem(logoConfig["name"].Vector(), CRandomGenerator::getDefault())),
Point(logoConfig["x"].Integer(), logoConfig["y"].Integer())
);
}
// Load sublogo
const auto& sublogoConfig = conf["sublogo"];
if (!sublogoConfig.isNull() && sublogoConfig["name"].isVector() && !sublogoConfig["name"].Vector().empty()) {
this->sublogo = std::make_shared<CPicture>(
ImagePath::fromJson(*RandomGeneratorUtil::nextItem(sublogoConfig["name"].Vector(), CRandomGenerator::getDefault())),
Point(sublogoConfig["x"].Integer(), sublogoConfig["y"].Integer())
);
}
// Load loadframe
const auto& loadframeConfig = conf["loadframe"];
if (loadframeConfig.isStruct()) {
this->loadFrame = std::make_shared<CPicture>(
ImagePath::fromJson(*RandomGeneratorUtil::nextItem(loadframeConfig["name"].Vector(), CRandomGenerator::getDefault())),
loadframeConfig["x"].Integer(),
loadframeConfig["y"].Integer()
);
}
// Load loadbar
const auto& loadbarConfig = conf["loadbar"];
if (loadbarConfig.isStruct()) {
AnimationPath loadbarPath = AnimationPath::fromJson(*RandomGeneratorUtil::nextItem(loadbarConfig["name"].Vector(), CRandomGenerator::getDefault()));
const int posx = loadbarConfig["x"].Integer();
const int posy = loadbarConfig["y"].Integer();
const int blockSize = loadbarConfig["size"].Integer();
const int blocksAmount = loadbarConfig["amount"].Integer();
for (int i = 0; i < blocksAmount; ++i)
{
progressBlocks.push_back(std::make_shared<CAnimImage>(loadbarPath, i, 0, posx + i * blockSize, posy));
progressBlocks.back()->deactivate();
progressBlocks.back()->visible = false;
}
}
}
CLoadingScreen::~CLoadingScreen()
{
}
void CLoadingScreen::tick(uint32_t msPassed)
{
if(!progressBlocks.empty())
{
int status = float(get()) / 255.f * progressBlocks.size();
for(int i = 0; i < status; ++i)
{
progressBlocks.at(i)->activate();
progressBlocks.at(i)->visible = true;
}
}
}
ImagePath CLoadingScreen::getBackground()
{
ImagePath fname = ImagePath::builtin("loadbar");
const auto & conf = CMainMenuConfig::get().getConfig()["loading"];
if(conf.isStruct())
{
if(conf["background"].isVector())
return ImagePath::fromJson(*RandomGeneratorUtil::nextItem(conf["background"].Vector(), CRandomGenerator::getDefault()));
if(conf["background"].isString())
return ImagePath::fromJson(conf["background"]);
return fname;
}
if(conf.isVector() && !conf.Vector().empty())
return ImagePath::fromJson(*RandomGeneratorUtil::nextItem(conf.Vector(), CRandomGenerator::getDefault()));
return fname;
}