mirror of
https://github.com/vcmi/vcmi.git
synced 2026-05-22 09:55:17 +02:00
Start game improvements
This commit is contained in:
@@ -795,6 +795,7 @@
|
||||
"vcmi.lobby.invite.header": "Pozvat hráče",
|
||||
"vcmi.lobby.invite.notification": "Hráč vás pozval do své soukromé místnosti. Nyní se k ní můžete připojit.",
|
||||
"vcmi.lobby.login.as": "Přihlásit se jako %s",
|
||||
"vcmi.lobby.login.connectionLost": "Spojení s herní lobby bylo ztraceno!",
|
||||
"vcmi.lobby.login.connecting": "Připojování...",
|
||||
"vcmi.lobby.login.create": "Nový účet",
|
||||
"vcmi.lobby.login.error": "Chyba při připojování: %s",
|
||||
@@ -828,6 +829,12 @@
|
||||
"vcmi.lobby.preview.version": "Verze hry:",
|
||||
"vcmi.lobby.pvp.coin.help": "Hodit mincí",
|
||||
"vcmi.lobby.pvp.coin.hover": "Mince",
|
||||
"vcmi.lobby.system.waitingForPlayers": "Čekání na připojení hráčů...",
|
||||
"vcmi.lobby.system.playerJoined": "Hráč %s se připojil",
|
||||
"vcmi.lobby.system.playerDisconnected": "Hráč %s se odpojil",
|
||||
"vcmi.lobby.system.hidingIncompatibleMaps": "Skrývám nekompatibilní mapy pro %d hráčů",
|
||||
"vcmi.lobby.system.unableStartMap": "Mapu nelze spustit!",
|
||||
"vcmi.lobby.system.reason": "Důvod: %s",
|
||||
"vcmi.lobby.pvp.randomTown.help": "Napsat náhodné město do chatu",
|
||||
"vcmi.lobby.pvp.randomTown.hover": "Náhodné město",
|
||||
"vcmi.lobby.pvp.randomTownVs.help": "Napsat 2 náhodná města do chatu",
|
||||
|
||||
@@ -795,6 +795,7 @@
|
||||
"vcmi.lobby.invite.header": "Invite Players",
|
||||
"vcmi.lobby.invite.notification": "Player has invited you to their game room. You can now join their private room.",
|
||||
"vcmi.lobby.login.as": "Login as %s",
|
||||
"vcmi.lobby.login.connectionLost": "Connection to game lobby was lost!",
|
||||
"vcmi.lobby.login.connecting": "Connecting...",
|
||||
"vcmi.lobby.login.create": "New Account",
|
||||
"vcmi.lobby.login.error": "Connection error: %s",
|
||||
@@ -828,6 +829,12 @@
|
||||
"vcmi.lobby.preview.version": "Game version:",
|
||||
"vcmi.lobby.pvp.coin.help": "Flips a coin",
|
||||
"vcmi.lobby.pvp.coin.hover": "Coin",
|
||||
"vcmi.lobby.system.waitingForPlayers": "Waiting for players to join...",
|
||||
"vcmi.lobby.system.playerJoined": "Player %s joined",
|
||||
"vcmi.lobby.system.playerDisconnected": "Player %s disconnected",
|
||||
"vcmi.lobby.system.hidingIncompatibleMaps": "Hiding incompatible maps for %d players",
|
||||
"vcmi.lobby.system.unableStartMap": "Unable to start map!",
|
||||
"vcmi.lobby.system.reason": "Reason: %s",
|
||||
"vcmi.lobby.pvp.randomTown.help": "Write a random town in the chat",
|
||||
"vcmi.lobby.pvp.randomTown.hover": "Random town",
|
||||
"vcmi.lobby.pvp.randomTownVs.help": "Write two random towns in the chat",
|
||||
|
||||
@@ -109,6 +109,7 @@ CServerHandler::CServerHandler()
|
||||
, screenType(ESelectionScreen::unknown)
|
||||
, serverMode(EServerMode::NONE)
|
||||
, loadMode(ELoadMode::NONE)
|
||||
, hotseatMode(false)
|
||||
, battleMode(false)
|
||||
, client(nullptr)
|
||||
{
|
||||
@@ -139,7 +140,10 @@ void CServerHandler::resetStateForLobby(EStartMode mode, ESelectionScreen screen
|
||||
hostClientId = GameConnectionID::INVALID;
|
||||
setState(EClientState::NONE);
|
||||
serverMode = newServerMode;
|
||||
loadMode = ELoadMode::NONE;
|
||||
mapToStart = nullptr;
|
||||
hotseatMode = false;
|
||||
battleMode = false;
|
||||
th = std::make_unique<CStopWatch>();
|
||||
logicConnection.reset();
|
||||
si = std::make_shared<StartInfo>();
|
||||
@@ -338,6 +342,15 @@ bool CServerHandler::isGuest() const
|
||||
return !logicConnection || hostClientId != logicConnection->connectionID;
|
||||
}
|
||||
|
||||
bool CServerHandler::hasRemoteClientInLobby() const
|
||||
{
|
||||
std::set<GameConnectionID> connectedClients;
|
||||
for(const auto & playerEntry : playerNames)
|
||||
connectedClients.insert(playerEntry.second.connection);
|
||||
|
||||
return connectedClients.size() > 1;
|
||||
}
|
||||
|
||||
const std::string & CServerHandler::getLocalHostname() const
|
||||
{
|
||||
return settings["server"]["localHostname"].String();
|
||||
@@ -586,7 +599,12 @@ bool CServerHandler::validateGameStart(bool allowOnlyAI) const
|
||||
catch(std::exception & e)
|
||||
{
|
||||
logGlobal->error("Exception during startScenario: %s", e.what());
|
||||
showServerError(std::string("Unable to start map!\nReason: ") + e.what());
|
||||
MetaString message;
|
||||
message.appendTextID("vcmi.lobby.system.unableStartMap");
|
||||
message.appendRawString("\n");
|
||||
message.appendTextID("vcmi.lobby.system.reason");
|
||||
message.replaceRawString(e.what());
|
||||
showServerError(message.toString());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -142,6 +142,7 @@ public:
|
||||
ESelectionScreen screenType; // To create lobby UI only after server is setup
|
||||
EServerMode serverMode;
|
||||
ELoadMode loadMode; // For saves filtering in SelectionTab
|
||||
bool hotseatMode;
|
||||
bool battleMode;
|
||||
////////////////////
|
||||
|
||||
@@ -171,6 +172,7 @@ public:
|
||||
|
||||
bool isHost() const;
|
||||
bool isGuest() const;
|
||||
bool hasRemoteClientInLobby() const;
|
||||
bool inLobbyRoom() const;
|
||||
bool inGame() const;
|
||||
|
||||
|
||||
@@ -427,7 +427,7 @@ void GlobalLobbyClient::onDisconnected(const std::shared_ptr<INetworkConnection>
|
||||
ENGINE->windows().popWindows(1);
|
||||
}
|
||||
|
||||
CInfoWindow::showInfoDialog("Connection to game lobby was lost!", {});
|
||||
CInfoWindow::showInfoDialog(LIBRARY->generaltexth->translate("vcmi.lobby.login.connectionLost"), {});
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::sendMessage(const JsonNode & data)
|
||||
|
||||
@@ -153,6 +153,9 @@ CBonusSelection::CBonusSelection()
|
||||
buttonExtraOptions = std::make_shared<CButton>(Point(643, 431), AnimationPath::builtin("GSPBUT2.DEF"), LIBRARY->generaltexth->zelp[46], [this]{ tabExtraOptions->setEnabled(!tabExtraOptions->isActive()); ENGINE->windows().totalRedraw(); }, EShortcut::LOBBY_EXTRA_OPTIONS);
|
||||
buttonExtraOptions->setTextOverlay(LIBRARY->generaltexth->translate("vcmi.optionsTab.extraOptions.hover"), FONT_SMALL, Colors::WHITE);
|
||||
}
|
||||
|
||||
// Ensure campaign map info is synchronized even if player doesn't click any region manually.
|
||||
GAME->server().setCampaignMap(GAME->server().campaignMap);
|
||||
}
|
||||
|
||||
void CBonusSelection::createBonusesIcons()
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "../CServerHandler.h"
|
||||
#include "../GameEngine.h"
|
||||
#include "../GameChatHandler.h"
|
||||
#include "../GameInstance.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
@@ -40,6 +41,7 @@ CLobbyScreen::CLobbyScreen(ESelectionScreen screenType, bool hideScreen)
|
||||
: CSelectionBase(screenType), bonusSel(nullptr)
|
||||
{
|
||||
OBJECT_CONSTRUCTION;
|
||||
addUsedEvents(TIME);
|
||||
tabSel = std::make_shared<SelectionTab>(screenType);
|
||||
curTab = tabSel;
|
||||
|
||||
@@ -59,7 +61,7 @@ CLobbyScreen::CLobbyScreen(ESelectionScreen screenType, bool hideScreen)
|
||||
if(settings["general"]["enableUiEnhancements"].Bool())
|
||||
{
|
||||
if(screenType == ESelectionScreen::newGame)
|
||||
buttonBattleMode = std::make_shared<CButton>(Point(619, 105), AnimationPath::builtin("GSPButton2Arrow"), CButton::tooltip("", LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyMode.help")), [this](){
|
||||
buttonBattleMode = std::make_shared<CButton>(Point(619, 80), AnimationPath::builtin("GSPButton2Arrow"), CButton::tooltip("", LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyMode.help")), [this](){
|
||||
updateAfterStateChange(); // creates tabBattleOnlyMode -> cannot created by init of object because GAME->server().isGuest() isn't valid at that point
|
||||
toggleTab(tabBattleOnlyMode);
|
||||
}, EShortcut::LOBBY_BATTLE_MODE);
|
||||
@@ -67,9 +69,9 @@ CLobbyScreen::CLobbyScreen(ESelectionScreen screenType, bool hideScreen)
|
||||
}
|
||||
};
|
||||
|
||||
if(screenType != ESelectionScreen::campaignList)
|
||||
if(screenType != ESelectionScreen::campaignList && GAME->server().loadMode == ELoadMode::MULTI && !GAME->server().hotseatMode)
|
||||
{
|
||||
buttonChat = std::make_shared<CButton>(Point(619, 80), AnimationPath::builtin("GSPBUT2.DEF"), LIBRARY->generaltexth->zelp[48], std::bind(&CLobbyScreen::toggleChat, this), EShortcut::LOBBY_TOGGLE_CHAT);
|
||||
buttonChat = std::make_shared<CButton>(Point(619, 105), AnimationPath::builtin("GSPBUT2.DEF"), LIBRARY->generaltexth->zelp[48], std::bind(&CLobbyScreen::toggleChat, this), EShortcut::LOBBY_TOGGLE_CHAT);
|
||||
buttonChat->setTextOverlay(LIBRARY->generaltexth->allTexts[532], FONT_SMALL, Colors::WHITE);
|
||||
}
|
||||
|
||||
@@ -128,6 +130,8 @@ CLobbyScreen::CLobbyScreen(ESelectionScreen screenType, bool hideScreen)
|
||||
blackScreen = std::make_shared<GraphicalPrimitiveCanvas>(Rect(Point(0, 0), pos.dimensions()));
|
||||
blackScreen->addBox(Point(0, 0), pos.dimensions(), Colors::BLACK);
|
||||
}
|
||||
|
||||
updateHostLobbyChatState();
|
||||
}
|
||||
|
||||
CLobbyScreen::~CLobbyScreen()
|
||||
@@ -137,6 +141,53 @@ CLobbyScreen::~CLobbyScreen()
|
||||
GAME->server().sendClientDisconnecting();
|
||||
}
|
||||
|
||||
bool CLobbyScreen::canStartLobbyGame() const
|
||||
{
|
||||
if(GAME->server().isGuest() || GAME->server().mi == nullptr)
|
||||
return false;
|
||||
|
||||
const bool isLanMultiplayerHost = GAME->server().loadMode == ELoadMode::MULTI
|
||||
&& GAME->server().serverMode == EServerMode::LOCAL
|
||||
&& !GAME->server().hotseatMode
|
||||
&& GAME->server().isHost();
|
||||
|
||||
if(isLanMultiplayerHost && !GAME->server().hasRemoteClientInLobby())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CLobbyScreen::updateHostLobbyChatState()
|
||||
{
|
||||
const bool isLanMultiplayerHost = buttonChat
|
||||
&& GAME->server().loadMode == ELoadMode::MULTI
|
||||
&& GAME->server().serverMode == EServerMode::LOCAL
|
||||
&& !GAME->server().hotseatMode
|
||||
&& GAME->server().isHost();
|
||||
|
||||
if(!isLanMultiplayerHost)
|
||||
return;
|
||||
|
||||
buttonChat->setTextOverlay(card->showChat ? LIBRARY->generaltexth->allTexts[531] : LIBRARY->generaltexth->allTexts[532], FONT_SMALL, Colors::WHITE);
|
||||
|
||||
if(GAME->server().hasRemoteClientInLobby())
|
||||
{
|
||||
waitingForPlayersMessageShown = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!waitingForPlayersMessageShown)
|
||||
{
|
||||
GAME->server().getGameChat().onNewLobbyMessageReceived("System", LIBRARY->generaltexth->translate("vcmi.lobby.system.waitingForPlayers"));
|
||||
waitingForPlayersMessageShown = true;
|
||||
}
|
||||
}
|
||||
|
||||
void CLobbyScreen::updateStartButtonState()
|
||||
{
|
||||
buttonStart->block(!canStartLobbyGame());
|
||||
}
|
||||
|
||||
void CLobbyScreen::toggleTab(std::shared_ptr<CIntObject> tab)
|
||||
{
|
||||
if(tab == curTab)
|
||||
@@ -161,13 +212,22 @@ void CLobbyScreen::toggleTab(std::shared_ptr<CIntObject> tab)
|
||||
}
|
||||
else
|
||||
{
|
||||
buttonStart->block(GAME->server().mi == nullptr || GAME->server().isGuest());
|
||||
updateStartButtonState();
|
||||
card->changeSelection();
|
||||
}
|
||||
|
||||
CSelectionBase::toggleTab(tab);
|
||||
}
|
||||
|
||||
void CLobbyScreen::tick(uint32_t msPassed)
|
||||
{
|
||||
CSelectionBase::tick(msPassed);
|
||||
|
||||
updateHostLobbyChatState();
|
||||
if(curTab != tabBattleOnlyMode)
|
||||
updateStartButtonState();
|
||||
}
|
||||
|
||||
void CLobbyScreen::start(bool campaign)
|
||||
{
|
||||
if(curTab == tabBattleOnlyMode)
|
||||
@@ -221,9 +281,11 @@ void CLobbyScreen::startScenario(bool allowOnlyAI)
|
||||
void CLobbyScreen::toggleMode(bool host)
|
||||
{
|
||||
tabSel->toggleMode();
|
||||
buttonStart->block(!host);
|
||||
if(screenType == ESelectionScreen::campaignList)
|
||||
{
|
||||
buttonStart->block(!host);
|
||||
return;
|
||||
}
|
||||
|
||||
auto buttonColor = host ? Colors::WHITE : Colors::ORANGE;
|
||||
buttonSelect->setTextOverlay(" " + LIBRARY->generaltexth->allTexts[500], FONT_SMALL, buttonColor);
|
||||
@@ -255,6 +317,8 @@ void CLobbyScreen::toggleMode(bool host)
|
||||
tabTurnOptions->recreate();
|
||||
tabExtraOptions->recreate();
|
||||
}
|
||||
|
||||
updateStartButtonState();
|
||||
}
|
||||
|
||||
void CLobbyScreen::toggleChat()
|
||||
@@ -269,6 +333,9 @@ void CLobbyScreen::toggleChat()
|
||||
void CLobbyScreen::updateAfterStateChange()
|
||||
{
|
||||
OBJECT_CONSTRUCTION;
|
||||
updateHostLobbyChatState();
|
||||
//tabSel->filter(-1);
|
||||
|
||||
if(!tabBattleOnlyMode)
|
||||
{
|
||||
tabBattleOnlyMode = std::make_shared<BattleOnlyModeTab>();
|
||||
@@ -298,9 +365,9 @@ void CLobbyScreen::updateAfterStateChange()
|
||||
tabExtraOptions->recreate();
|
||||
}
|
||||
|
||||
if(curTab && curTab != tabBattleOnlyMode)
|
||||
if(curTab != tabBattleOnlyMode)
|
||||
{
|
||||
buttonStart->block(GAME->server().mi == nullptr || GAME->server().isGuest());
|
||||
updateStartButtonState();
|
||||
card->changeSelection();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,12 @@ class GraphicalPrimitiveCanvas;
|
||||
|
||||
class CLobbyScreen final : public CSelectionBase
|
||||
{
|
||||
bool waitingForPlayersMessageShown = false;
|
||||
|
||||
bool canStartLobbyGame() const;
|
||||
void updateHostLobbyChatState();
|
||||
void updateStartButtonState();
|
||||
|
||||
public:
|
||||
std::shared_ptr<CButton> buttonChat;
|
||||
std::shared_ptr<GraphicalPrimitiveCanvas> blackScreen;
|
||||
@@ -23,6 +29,7 @@ public:
|
||||
CLobbyScreen(ESelectionScreen type, bool hideScreen = false);
|
||||
~CLobbyScreen();
|
||||
void toggleTab(std::shared_ptr<CIntObject> tab) final;
|
||||
void tick(uint32_t msPassed) override;
|
||||
void start(bool campaign);
|
||||
void startCampaign();
|
||||
void startScenario(bool allowOnlyAI = false);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../GameChatHandler.h"
|
||||
#include "../GameEngine.h"
|
||||
#include "../GameInstance.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
@@ -608,10 +609,19 @@ void SelectionTab::filter(int size, bool selectFirst)
|
||||
if(buttonDeleteMode)
|
||||
buttonDeleteMode->setEnabled(tabType != ESelectionScreen::newGame || showRandom);
|
||||
|
||||
size_t hiddenIncompatibleMaps = 0;
|
||||
size_t requiredHumanPlayers = getRequiredHumanPlayers();
|
||||
|
||||
for(auto elem : allItems)
|
||||
{
|
||||
if((elem->mapHeader && (!size || elem->mapHeader->width == size)) || tabType == ESelectionScreen::campaignList)
|
||||
{
|
||||
if(!isMapCompatibleWithLobbyPlayerCount(*elem))
|
||||
{
|
||||
++hiddenIncompatibleMaps;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(showRandom)
|
||||
curFolder = "RandomMaps/";
|
||||
|
||||
@@ -645,6 +655,26 @@ void SelectionTab::filter(int size, bool selectFirst)
|
||||
}
|
||||
}
|
||||
|
||||
if(hiddenIncompatibleMaps && tabType == ESelectionScreen::newGame && GAME->server().loadMode == ELoadMode::MULTI)
|
||||
{
|
||||
MetaString warningText;
|
||||
warningText.appendTextID("vcmi.lobby.system.hidingIncompatibleMaps");
|
||||
warningText.replaceNumber(requiredHumanPlayers);
|
||||
const std::string warningTextFormatted = warningText.toString();
|
||||
|
||||
if(lastCompatibilityNotice != warningTextFormatted)
|
||||
{
|
||||
logGlobal->info("%s", warningTextFormatted);
|
||||
if(!GAME->server().hotseatMode)
|
||||
GAME->server().getGameChat().onNewLobbyMessageReceived("System", warningTextFormatted);
|
||||
lastCompatibilityNotice = warningTextFormatted;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lastCompatibilityNotice.clear();
|
||||
}
|
||||
|
||||
if(curItems.size())
|
||||
{
|
||||
slider->block(false);
|
||||
@@ -660,6 +690,25 @@ void SelectionTab::filter(int size, bool selectFirst)
|
||||
selectAbs(firstPos);
|
||||
}
|
||||
}
|
||||
else if(tabType == ESelectionScreen::newGame || tabType == ESelectionScreen::campaignList)
|
||||
{
|
||||
const std::string selectedMapFileURI = GAME->server().mi ? GAME->server().mi->fileURI : "";
|
||||
auto selectedMapIt = boost::range::find_if(curItems, [&selectedMapFileURI](std::shared_ptr<ElementInfo> e)
|
||||
{
|
||||
return !e->isFolder && e->fileURI == selectedMapFileURI;
|
||||
});
|
||||
|
||||
if(selectedMapIt == curItems.end())
|
||||
{
|
||||
int firstPos = boost::range::find_if(curItems, [](std::shared_ptr<ElementInfo> e) { return !e->isFolder; }) - curItems.begin();
|
||||
if(firstPos < curItems.size())
|
||||
{
|
||||
slider->scrollTo(firstPos);
|
||||
callOnSelect(curItems[firstPos]);
|
||||
selectAbs(firstPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -943,6 +992,29 @@ bool SelectionTab::isMapSupported(const CMapInfo & info)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SelectionTab::isMapCompatibleWithLobbyPlayerCount(const ElementInfo & info) const
|
||||
{
|
||||
if(tabType != ESelectionScreen::newGame || GAME->server().loadMode != ELoadMode::MULTI || !info.mapHeader)
|
||||
return true;
|
||||
|
||||
const auto requiredHumanPlayers = getRequiredHumanPlayers();
|
||||
size_t supportedHumanPlayers = 0;
|
||||
|
||||
for(const auto & player : info.mapHeader->players)
|
||||
{
|
||||
if(player.canHumanPlay)
|
||||
++supportedHumanPlayers;
|
||||
}
|
||||
|
||||
return supportedHumanPlayers >= requiredHumanPlayers;
|
||||
}
|
||||
|
||||
size_t SelectionTab::getRequiredHumanPlayers() const
|
||||
{
|
||||
const size_t minimumPlayers = (GAME->server().loadMode == ELoadMode::MULTI || GAME->server().hotseatMode) ? 2 : 1;
|
||||
return std::max(minimumPlayers, GAME->server().playerNames.size());
|
||||
}
|
||||
|
||||
void SelectionTab::parseMaps(const std::unordered_set<ResourcePath> & files)
|
||||
{
|
||||
logGlobal->debug("Parsing %d maps", files.size());
|
||||
|
||||
@@ -90,6 +90,7 @@ public:
|
||||
bool sortModeAscending;
|
||||
int currentMapSizeFilter = 0;
|
||||
bool showRandom;
|
||||
std::string lastCompatibilityNotice;
|
||||
|
||||
std::shared_ptr<CTextInput> inputName;
|
||||
|
||||
@@ -133,6 +134,8 @@ private:
|
||||
std::shared_ptr<CButton> buttonCampaignSet;
|
||||
|
||||
auto checkSubfolder(std::string path);
|
||||
size_t getRequiredHumanPlayers() const;
|
||||
bool isMapCompatibleWithLobbyPlayerCount(const ElementInfo & info) const;
|
||||
|
||||
bool isMapSupported(const CMapInfo & info);
|
||||
void parseMaps(const std::unordered_set<ResourcePath> & files);
|
||||
|
||||
@@ -398,10 +398,11 @@ void CMainMenu::makeActiveInterface()
|
||||
menu->switchToTab(menu->getActiveTab());
|
||||
}
|
||||
|
||||
void CMainMenu::openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> & names, ELoadMode loadMode, bool battleMode, std::string server, ui16 port)
|
||||
void CMainMenu::openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> & names, ELoadMode loadMode, bool battleMode, bool hotseatMode, std::string server, ui16 port)
|
||||
{
|
||||
GAME->server().resetStateForLobby(screenType == ESelectionScreen::newGame ? EStartMode::NEW_GAME : EStartMode::LOAD_GAME, screenType, EServerMode::LOCAL, names);
|
||||
GAME->server().loadMode = loadMode;
|
||||
GAME->server().hotseatMode = hotseatMode;
|
||||
GAME->server().battleMode = battleMode;
|
||||
|
||||
ENGINE->windows().createAndPushWindow<CSimpleJoinScreen>(host, server, port);
|
||||
@@ -590,7 +591,7 @@ void JoinScreen::onServerDiscovered(const DiscoveredServer & server)
|
||||
auto savedScreenType = screenType;
|
||||
auto savedPlayerNames = playerNames;
|
||||
close();
|
||||
CMainMenu::openLobby(savedScreenType, false, savedPlayerNames, ELoadMode::MULTI, false, server.address, server.port);
|
||||
CMainMenu::openLobby(savedScreenType, false, savedPlayerNames, ELoadMode::MULTI, false, false, server.address, server.port);
|
||||
});
|
||||
button->setTextOverlay(LIBRARY->generaltexth->translate("vcmi.mainMenu.join"), FONT_SMALL, Colors::WHITE);
|
||||
buttonsJoin.push_back(button);
|
||||
@@ -600,7 +601,7 @@ void JoinScreen::onServerDiscovered(const DiscoveredServer & server)
|
||||
}
|
||||
|
||||
CMultiPlayers::CMultiPlayers(const std::vector<std::string>& playerNames, ESelectionScreen ScreenType, bool Host, ELoadMode LoadMode, EShortcut shortcut)
|
||||
: loadMode(LoadMode), screenType(ScreenType), host(Host)
|
||||
: host(Host), hotseat(shortcut == EShortcut::MAIN_MENU_HOTSEAT), loadMode(LoadMode), screenType(ScreenType)
|
||||
{
|
||||
OBJECT_CONSTRUCTION;
|
||||
background = std::make_shared<CPicture>(ImagePath::builtin("MUHOTSEA.bmp"));
|
||||
@@ -637,13 +638,24 @@ CMultiPlayers::CMultiPlayers(const std::vector<std::string>& playerNames, ESelec
|
||||
{
|
||||
inputNames[i]->setText(playerNames[i]);
|
||||
}
|
||||
|
||||
buttonOk->block(hotseat && countEnteredNames() < 2);
|
||||
#ifndef VCMI_MOBILE
|
||||
inputNames[0]->giveFocus();
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t CMultiPlayers::countEnteredNames() const
|
||||
{
|
||||
return std::count_if(inputNames.begin(), inputNames.end(), [](const auto & playerName)
|
||||
{
|
||||
return playerName->getText().length();
|
||||
});
|
||||
}
|
||||
|
||||
void CMultiPlayers::onChange(std::string newText)
|
||||
{
|
||||
buttonOk->block(hotseat && countEnteredNames() < 2);
|
||||
}
|
||||
|
||||
void CMultiPlayers::enterSelectionScreen()
|
||||
@@ -675,6 +687,9 @@ void CMultiPlayers::enterSelectionScreen()
|
||||
playerName->clear();
|
||||
}
|
||||
|
||||
if(hotseat && playerNames.size() < 2)
|
||||
return;
|
||||
|
||||
if(!host)
|
||||
{
|
||||
auto savedScreenType = screenType;
|
||||
@@ -683,7 +698,7 @@ void CMultiPlayers::enterSelectionScreen()
|
||||
ENGINE->windows().createAndPushWindow<JoinScreen>(savedScreenType, savedPlayerNames);
|
||||
return;
|
||||
}
|
||||
CMainMenu::openLobby(screenType, host, playerNames, loadMode, false);
|
||||
CMainMenu::openLobby(screenType, host, playerNames, loadMode, false, hotseat);
|
||||
}
|
||||
|
||||
CSimpleJoinScreen::CSimpleJoinScreen(bool host, std::string server, ui16 port)
|
||||
|
||||
@@ -131,6 +131,7 @@ public:
|
||||
class CMultiPlayers : public WindowBase
|
||||
{
|
||||
bool host;
|
||||
bool hotseat;
|
||||
ELoadMode loadMode;
|
||||
ESelectionScreen screenType;
|
||||
std::shared_ptr<CPicture> background;
|
||||
@@ -140,6 +141,7 @@ class CMultiPlayers : public WindowBase
|
||||
std::shared_ptr<CButton> buttonCancel;
|
||||
std::shared_ptr<CGStatusBar> statusBar;
|
||||
|
||||
size_t countEnteredNames() const;
|
||||
void onChange(std::string newText);
|
||||
void enterSelectionScreen();
|
||||
|
||||
@@ -178,7 +180,7 @@ public:
|
||||
void activate() override;
|
||||
void onScreenResize() override;
|
||||
void makeActiveInterface();
|
||||
static void openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> & names, ELoadMode loadMode, bool battleMode, std::string server = {}, ui16 port = 0);
|
||||
static void openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> & names, ELoadMode loadMode, bool battleMode, bool hotseatMode = false, std::string server = {}, ui16 port = 0);
|
||||
static void openCampaignLobby(const std::string & campaignFileName, std::string campaignSet = "");
|
||||
static void openCampaignLobby(std::shared_ptr<CampaignState> campaign);
|
||||
static void startTutorial();
|
||||
@@ -228,4 +230,3 @@ public:
|
||||
void tick(uint32_t msPassed) override;
|
||||
};
|
||||
|
||||
|
||||
|
||||
+44
-2
@@ -495,7 +495,11 @@ void CVCMIServer::clientConnected(std::shared_ptr<GameConnection> c, std::vector
|
||||
cp.connection = c->connectionID;
|
||||
cp.name = name;
|
||||
playerNames.try_emplace(id, cp);
|
||||
announceTxt(boost::str(boost::format("%s (pid %d cid %d) joins the game") % name % static_cast<int>(id) % static_cast<int>(c->connectionID)));
|
||||
logNetwork->info("Player joined lobby: name='%s', playerId=%d, connectionId=%d", name, static_cast<int>(id), static_cast<int>(c->connectionID));
|
||||
MetaString joinMessage;
|
||||
joinMessage.appendTextID("vcmi.lobby.system.playerJoined");
|
||||
joinMessage.replaceRawString(name);
|
||||
announceTxt(joinMessage);
|
||||
|
||||
//put new player in first slot with AI
|
||||
for(auto & elem : si->playerInfos)
|
||||
@@ -515,6 +519,45 @@ void CVCMIServer::clientDisconnected(std::shared_ptr<GameConnection> connection)
|
||||
logGlobal->trace("Received disconnection request");
|
||||
vstd::erase(activeConnections, connection);
|
||||
|
||||
std::vector<PlayerConnectionID> disconnectedPlayerIds;
|
||||
std::vector<std::string> disconnectedPlayerNames;
|
||||
for(const auto & playerEntry : playerNames)
|
||||
{
|
||||
if(playerEntry.second.connection == connection->connectionID)
|
||||
{
|
||||
disconnectedPlayerIds.push_back(playerEntry.first);
|
||||
disconnectedPlayerNames.push_back(playerEntry.second.name);
|
||||
}
|
||||
}
|
||||
|
||||
for(const auto & playerName : disconnectedPlayerNames)
|
||||
{
|
||||
logNetwork->info("Player disconnected from lobby: name='%s', connectionId=%d", playerName, static_cast<int>(connection->connectionID));
|
||||
MetaString disconnectMessage;
|
||||
disconnectMessage.appendTextID("vcmi.lobby.system.playerDisconnected");
|
||||
disconnectMessage.replaceRawString(playerName);
|
||||
announceTxt(disconnectMessage);
|
||||
}
|
||||
|
||||
if(disconnectedPlayerNames.empty())
|
||||
logNetwork->info("Connection %d disconnected from lobby with no mapped player names", static_cast<int>(connection->connectionID));
|
||||
|
||||
for(const auto & playerId : disconnectedPlayerIds)
|
||||
playerNames.erase(playerId);
|
||||
|
||||
if(!disconnectedPlayerIds.empty())
|
||||
{
|
||||
for(auto & playerInfoEntry : si->playerInfos)
|
||||
{
|
||||
auto & connectedPlayerIDs = playerInfoEntry.second.connectedPlayerIDs;
|
||||
for(const auto & playerId : disconnectedPlayerIds)
|
||||
connectedPlayerIDs.erase(playerId);
|
||||
|
||||
if(connectedPlayerIDs.empty())
|
||||
setPlayerConnectedId(playerInfoEntry.second, PlayerConnectionID::PLAYER_AI);
|
||||
}
|
||||
}
|
||||
|
||||
if(activeConnections.empty() || hostClientId == connection->connectionID)
|
||||
{
|
||||
setState(EServerState::SHUTDOWN);
|
||||
@@ -1196,4 +1239,3 @@ void CVCMIServer::sendPack(CPackForClient & pack, GameConnectionID connectionID)
|
||||
if (c->connectionID == connectionID)
|
||||
c->sendPack(pack);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user