mirror of
https://github.com/vcmi/vcmi.git
synced 2025-03-21 21:17:49 +02:00
All assets generation (large spellbook, terrain animations, etc) are now done in memory and used as it, without saving to disk. This should slightly improve load times since there is no encode png / decode png, and should help with avoiding strange bug when vcmi fails to load recently saved assets. If needed, such assets can be force-dumped on disk using already existing console command
792 lines
25 KiB
C++
792 lines
25 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 "../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();
|
|
|
|
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["game-select"].Vector().empty())
|
|
handleFatalError("Main menu config is invalid or corrupted. Please disable any mods or reinstall VCMI", 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();
|
|
|
|
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);
|
|
|
|
if (multiplayerConfig.isString())
|
|
picture = std::make_shared<CPicture>(ImagePath::fromJson(multiplayerConfig), 16, 77);
|
|
|
|
if (!picture)
|
|
{
|
|
picture = std::make_shared<CPicture>(ImagePath::builtin("MUMAP.bmp"), 16, 77);
|
|
logGlobal->error("Failed to load multiplayer picture");
|
|
}
|
|
|
|
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"];
|
|
const auto& nameConfig = conf["name"];
|
|
|
|
AnimationPath animationPath;
|
|
if (nameConfig.isVector() && !nameConfig.Vector().empty())
|
|
animationPath = AnimationPath::fromJson(*RandomGeneratorUtil::nextItem(nameConfig.Vector(), CRandomGenerator::getDefault()));
|
|
|
|
if (nameConfig.isString())
|
|
animationPath = AnimationPath::fromJson(nameConfig);
|
|
|
|
if (conf.isStruct())
|
|
{
|
|
const int posx = conf["x"].Integer();
|
|
const int posy = conf["y"].Integer();
|
|
const int blockSize = conf["size"].Integer();
|
|
const int blocksAmount = conf["amount"].Integer();
|
|
|
|
for (int i = 0; i < blocksAmount; ++i)
|
|
{
|
|
progressBlocks.push_back(std::make_shared<CAnimImage>(animationPath, 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;
|
|
}
|