diff --git a/README.md b/README.md
index 84eba7fd2..1db15f9ca 100644
--- a/README.md
+++ b/README.md
@@ -22,6 +22,11 @@ following resources to learn the basic gameplay concepts:
You can play singleplayer levels using the Skirmish menu, or host/join a
multiplayer game by using the corresponding menus.
+Use Load Game in the game options, or F8 while hosting a local game, to open
+the saved-game browser; Back preserves the current game until another save is
+selected. Loaded single-player saves resume their stored side without a new
+seat-selection step. Long save descriptions can be scrolled.
+
### Be part of the community
As free software aficionados, we value community-based development and
diff --git a/gui/MenuLoad.layout b/gui/MenuLoad.layout
index 94d383fcc..b08035fe6 100644
--- a/gui/MenuLoad.layout
+++ b/gui/MenuLoad.layout
@@ -27,18 +27,20 @@
-
+
-
-
+
+
-
+
-
+
+
+
diff --git a/gui/OD.looknfeel b/gui/OD.looknfeel
index 15c7c8d4a..608351d2a 100644
--- a/gui/OD.looknfeel
+++ b/gui/OD.looknfeel
@@ -4772,4 +4772,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/gui/ODSkin.scheme b/gui/ODSkin.scheme
index f3fc54304..801554cb8 100644
--- a/gui/ODSkin.scheme
+++ b/gui/ODSkin.scheme
@@ -57,4 +57,6 @@
+
+
diff --git a/gui/WindowGameOptions.layout b/gui/WindowGameOptions.layout
index 879699faa..cb3087722 100644
--- a/gui/WindowGameOptions.layout
+++ b/gui/WindowGameOptions.layout
@@ -25,8 +25,7 @@
-
-
+
diff --git a/source/modes/AbstractApplicationMode.cpp b/source/modes/AbstractApplicationMode.cpp
index e93a4e54f..ef4aee4c9 100755
--- a/source/modes/AbstractApplicationMode.cpp
+++ b/source/modes/AbstractApplicationMode.cpp
@@ -24,6 +24,29 @@
#include
#include
#include
+#include
+#include
+#include
+
+#include
+
+namespace
+{
+void collectEscapeWindows(CEGUI::Window* window, std::vector& windows)
+{
+ if(window == nullptr || !window->isVisible() || window->isDisabled())
+ return;
+
+ CEGUI::Combobox* combo = dynamic_cast(window);
+ if(dynamic_cast(window) != nullptr ||
+ dynamic_cast(window) != nullptr ||
+ (combo != nullptr && combo->isDropDownListVisible()))
+ windows.push_back(window);
+
+ for(size_t i = 0; i < window->getChildCount(); ++i)
+ collectEscapeWindows(window->getChildAtIdx(i), windows);
+}
+}
AbstractApplicationMode::~AbstractApplicationMode()
{
@@ -78,6 +101,11 @@ bool AbstractApplicationMode::keyPressed(const OIS::KeyEvent& arg)
{
switch (arg.key)
{
+ case OIS::KC_ESCAPE:
+ if(!CEGUI::System::getSingleton().getDefaultGUIContext().injectKeyDown(CEGUI::Key::Escape) &&
+ !closeTopWindow() && mModeType != ModeManager::ADVERTISMENT)
+ goBack();
+ break;
default:
CEGUI::System::getSingleton().getDefaultGUIContext().injectChar(
static_cast(arg.text));
@@ -104,3 +132,34 @@ bool AbstractApplicationMode::goBack(const CEGUI::EventArgs&)
mModeManager->requestPreviousMode();
return true;
}
+
+bool AbstractApplicationMode::closeTopWindow()
+{
+ CEGUI::GUIContext& context = CEGUI::System::getSingleton().getDefaultGUIContext();
+ std::vector windows;
+ collectEscapeWindows(context.getModalWindow() != nullptr ?
+ context.getModalWindow() : context.getRootWindow(), windows);
+ std::sort(windows.begin(), windows.end(), [](CEGUI::Window* left, CEGUI::Window* right)
+ {
+ return left->isInFront(*right);
+ });
+
+ for(CEGUI::Window* window : windows)
+ {
+ if(CEGUI::Combobox* combo = dynamic_cast(window))
+ {
+ combo->hideDropList();
+ return true;
+ }
+ if(CEGUI::PopupMenu* popup = dynamic_cast(window))
+ {
+ popup->closePopupMenu();
+ return true;
+ }
+ CEGUI::WindowEventArgs args(window);
+ window->fireEvent(CEGUI::FrameWindow::EventCloseClicked, args);
+ if(args.handled != 0)
+ return true;
+ }
+ return false;
+}
diff --git a/source/modes/AbstractApplicationMode.h b/source/modes/AbstractApplicationMode.h
index ffee186cb..30fda6e62 100755
--- a/source/modes/AbstractApplicationMode.h
+++ b/source/modes/AbstractApplicationMode.h
@@ -126,6 +126,9 @@ class AbstractApplicationMode :
{}
protected:
+ //! Close the frontmost visible GUI window through its existing cancel handler.
+ bool closeTopWindow();
+
ModeManager& getModeManager()
{
return *mModeManager;
diff --git a/source/modes/EditorMode.cpp b/source/modes/EditorMode.cpp
index bafb65c33..ee5f19fd4 100755
--- a/source/modes/EditorMode.cpp
+++ b/source/modes/EditorMode.cpp
@@ -340,11 +340,6 @@ EditorMode::EditorMode(ModeManager* modeManager):
CEGUI::FrameWindow::EventCloseClicked,
CEGUI::Event::Subscriber(&EditorMode::toggleOptionsWindow, this)
));
- addEventConnection(
- mRootWindow->getChild("EditorOptionsWindow")->subscribeEvent(
- CEGUI::FrameWindow::EventCloseClicked,
- CEGUI::Event::Subscriber(&EditorMode::toggleOptionsWindow, this)
- ));
addEventConnection(
mRootWindow->getChild("EditorOptionsWindow/SaveLevelButton")
->subscribeEvent(
@@ -1497,9 +1492,14 @@ bool EditorMode::hidePortalWaveWindow(const CEGUI::EventArgs& /*arg*/)
bool EditorMode::keyPressed(const OIS::KeyEvent &arg)
{
// Inject key to the gui currently displayed
- CEGUI::System::getSingleton().getDefaultGUIContext().injectKeyDown(static_cast(arg.key));
+ const bool guiHandledKey = CEGUI::System::getSingleton().getDefaultGUIContext().injectKeyDown(
+ static_cast(arg.key));
CEGUI::System::getSingleton().getDefaultGUIContext().injectChar(arg.text);
+ if(arg.key == OIS::KC_ESCAPE && mCurrentInputMode != InputModeConsole &&
+ mCurrentInputMode != InputModeChat && (guiHandledKey || closeTopWindow()))
+ return true;
+
if (mCurrentInputMode == InputModeChat || mCurrentInputMode == InputModeSave || mCurrentInputMode == InputModeLoad || mCurrentInputMode == InputModeNew)
return true;
diff --git a/source/modes/GameMode.cpp b/source/modes/GameMode.cpp
index d364c92ca..5ea58dd61 100755
--- a/source/modes/GameMode.cpp
+++ b/source/modes/GameMode.cpp
@@ -30,6 +30,7 @@
#include "gamemap/Pathfinding.h"
#include "modes/GameEditorModeConsole.h"
#include "modes/InputBridge.h"
+#include "modes/MenuModeLoad.h"
#include "network/ChatEventMessage.h"
#include "network/ODClient.h"
#include "network/ODServer.h"
@@ -210,6 +211,10 @@ GameMode::GameMode(ModeManager *modeManager):
)
);
saveGameButtonWindow->setEnabled(ODServer::getSingleton().isConnected());
+ CEGUI::Window* loadGameButtonWindow = guiSheet->getChild("GameOptionsWindow/LoadGameButton");
+ addEventConnection(loadGameButtonWindow->subscribeEvent(CEGUI::PushButton::EventClicked,
+ CEGUI::Event::Subscriber(&GameMode::loadGame, this)));
+ loadGameButtonWindow->setEnabled(ODServer::getSingleton().isConnected());
addEventConnection(
guiSheet->getChild("GameOptionsWindow/SettingsButton")->subscribeEvent(
CEGUI::PushButton::EventClicked,
@@ -792,8 +797,11 @@ bool GameMode::mouseReleased(const OIS::MouseEvent &arg, OIS::MouseButtonID id)
bool GameMode::keyPressed(const OIS::KeyEvent& arg)
{
+ if(mLoadMenu && mLoadMenu->isOpenInGame())
+ return mLoadMenu->keyPressed(arg);
// Inject key to Gui
- CEGUI::System::getSingleton().getDefaultGUIContext().injectKeyDown(static_cast(arg.key));
+ const bool guiHandledKey = CEGUI::System::getSingleton().getDefaultGUIContext().injectKeyDown(
+ static_cast(arg.key));
if (arg.text != 0 && !getConsole()->isFreshlyEnabled())
{
CEGUI::System::getSingleton().getDefaultGUIContext().injectChar(arg.text);
@@ -807,6 +815,8 @@ bool GameMode::keyPressed(const OIS::KeyEvent& arg)
return getConsole()->keyPressed(arg);
case InputModeNormal:
default:
+ if(arg.key == OIS::KC_ESCAPE && guiHandledKey)
+ return true;
return keyPressedNormal(arg);
}
}
@@ -837,6 +847,10 @@ bool GameMode::keyPressedNormal(const OIS::KeyEvent &arg)
saveGame();
break;
+ case OIS::KC_F8:
+ loadGame();
+ break;
+
case OIS::KC_F9:
RenderManager::getSingleton().rrToggleHandSelectorVisibility();
break;
@@ -934,8 +948,15 @@ bool GameMode::keyPressedNormal(const OIS::KeyEvent &arg)
break;
}
- // Quit the game
+ // Close one GUI layer before considering a new exit confirmation.
case OIS::KC_ESCAPE:
+ if(closeTopWindow())
+ break;
+ if(mRootWindow->getChild("GameEventText")->isVisible())
+ {
+ mRootWindow->getChild("GameEventText")->hide();
+ break;
+ }
mExitToDesktop = false;
popupExit(!mGameMap->getGamePaused());
break;
@@ -1404,6 +1425,18 @@ bool GameMode::showSkillFromOptions(const CEGUI::EventArgs& /*e*/)
return true;
}
+bool GameMode::loadGame(const CEGUI::EventArgs& /*e*/)
+{
+ if(!ODServer::getSingleton().isConnected())
+ return true;
+ if(!mLoadMenu)
+ mLoadMenu.reset(new MenuModeLoad(&getModeManager(), true));
+ // Retain the options window so returning from the browser restores its caller.
+ showOptionsWindow();
+ mLoadMenu->activate();
+ return true;
+}
+
bool GameMode::saveGame(const CEGUI::EventArgs& /*e*/)
{
// We can save if launching in server mode only
diff --git a/source/modes/GameMode.h b/source/modes/GameMode.h
index 1451eb02d..b174aa958 100755
--- a/source/modes/GameMode.h
+++ b/source/modes/GameMode.h
@@ -33,6 +33,7 @@ class Window;
}
class Creature;
+class MenuModeLoad;
enum class SpellType;
enum class SkillType;
@@ -208,6 +209,7 @@ class GameMode final : public GameEditorModeBase, public InputCommand
bool showObjectivesFromOptions(const CEGUI::EventArgs& e = {});
bool showSkillFromOptions(const CEGUI::EventArgs& e = {});
bool saveGame(const CEGUI::EventArgs& e = {});
+ bool loadGame(const CEGUI::EventArgs& e = {});
bool showSettingsFromOptions(const CEGUI::EventArgs& e = {});
//! \brief Handle the keyboard input in normal mode
@@ -220,6 +222,7 @@ class GameMode final : public GameEditorModeBase, public InputCommand
virtual bool keyReleasedNormal (const OIS::KeyEvent &arg);
private:
+ std::unique_ptr mLoadMenu;
//! \brief Whether the pending exit confirmation should leave to the desktop
//! rather than back to the main menu. Set by the button that opened the
//! confirmation popup.
diff --git a/source/modes/MenuModeLoad.cpp b/source/modes/MenuModeLoad.cpp
index ccd3f0104..6dc714064 100755
--- a/source/modes/MenuModeLoad.cpp
+++ b/source/modes/MenuModeLoad.cpp
@@ -33,19 +33,21 @@
#include "utils/ResourceManager.h"
#include
+#include
#include "boost/filesystem.hpp"
const std::string SAVEGAME_EXTENSION = ".level";
-MenuModeLoad::MenuModeLoad(ModeManager *modeManager):
- AbstractApplicationMode(modeManager, ModeManager::MENU_LOAD_SAVEDGAME)
+MenuModeLoad::MenuModeLoad(ModeManager *modeManager, bool inGame, const std::string& savedGame):
+ AbstractApplicationMode(modeManager, ModeManager::MENU_LOAD_SAVEDGAME),
+ mInGame(inGame),
+ mSavedGame(savedGame)
{
CEGUI::Window* window = modeManager->getGui().getGuiSheet(Gui::guiSheet::loadSavedGameMenu);
addEventConnection(
window->getChild("LevelWindowFrame/BackButton")->subscribeEvent(
CEGUI::PushButton::EventClicked,
- CEGUI::Event::Subscriber(&AbstractApplicationMode::goBack,
- static_cast(this))
+ CEGUI::Event::Subscriber(&MenuModeLoad::closeBrowser, this)
)
);
addEventConnection(
@@ -75,26 +77,55 @@ MenuModeLoad::MenuModeLoad(ModeManager *modeManager):
addEventConnection(
window->getChild("LevelWindowFrame")->subscribeEvent(
CEGUI::FrameWindow::EventCloseClicked,
- CEGUI::Event::Subscriber(&AbstractApplicationMode::goBack,
- static_cast(this))
+ CEGUI::Event::Subscriber(&MenuModeLoad::closeBrowser, this)
)
);
}
+MenuModeLoad::~MenuModeLoad()
+{
+ if(isOpenInGame())
+ closeBrowser();
+}
+
+bool MenuModeLoad::closeBrowser(const CEGUI::EventArgs&)
+{
+ if(!mInGame)
+ return goBack();
+ mOpen = false;
+ ODFrameListener::getSingleton().getClientGameMap()->setGamePaused(mWasPaused);
+ getModeManager().getGui().loadGuiSheet(Gui::inGameMenu);
+ return true;
+}
+
void MenuModeLoad::activate()
{
// Loads the corresponding Gui sheet.
getModeManager().getGui().loadGuiSheet(Gui::guiSheet::loadSavedGameMenu);
- giveFocus();
-
- // Play the main menu music
- MusicPlayer::getSingleton().play(ConfigManager::getSingleton().getMainMenuMusic());
-
-
GameMap* gameMap = ODFrameListener::getSingleton().getClientGameMap();
- gameMap->clearAll();
- gameMap->setGamePaused(true);
+ CEGUI::Window* sheet = getModeManager().getGui().getGuiSheet(Gui::loadSavedGameMenu);
+ sheet->getChild("WelcomeBanner")->setVisible(!mInGame);
+ sheet->getChild("VersionText")->setVisible(!mInGame);
+ if(mInGame)
+ {
+ if(!mOpen)
+ mWasPaused = gameMap->getGamePaused();
+ mOpen = true;
+ gameMap->setGamePaused(true);
+ }
+ else
+ {
+ giveFocus();
+ MusicPlayer::getSingleton().play(ConfigManager::getSingleton().getMainMenuMusic());
+ gameMap->clearAll();
+ gameMap->setGamePaused(true);
+ if(!mSavedGame.empty())
+ {
+ ODFrameListener::getSingleton().stopGameRenderer();
+ ODFrameListener::getSingleton().createMainMenuScene();
+ }
+ }
CEGUI::Window* tmpWin = getModeManager().getGui().getGuiSheet(Gui::loadSavedGameMenu)->getChild("LevelWindowFrame/SaveGameSelect");
CEGUI::Listbox* levelSelectList = static_cast(tmpWin);
@@ -103,6 +134,7 @@ void MenuModeLoad::activate()
tmpWin->hide();
mFilesList.clear();
levelSelectList->resetList();
+ sheet->getChild("LevelWindowFrame/MapDescriptionText")->setText("");
std::string levelPath = ResourceManager::getSingleton().getSaveGamePath();
if(Helper::fillFilesList(levelPath, mFilesList, SAVEGAME_EXTENSION))
@@ -116,6 +148,13 @@ void MenuModeLoad::activate()
levelSelectList->addItem(item);
}
}
+ if(!mSavedGame.empty())
+ {
+ const std::string filename = mSavedGame;
+ mSavedGame.clear();
+ launchSavedGame(filename);
+ }
+
}
bool MenuModeLoad::launchSelectedButtonPressed(const CEGUI::EventArgs&)
@@ -131,18 +170,27 @@ bool MenuModeLoad::launchSelectedButtonPressed(const CEGUI::EventArgs&)
return true;
}
+ const uint32_t id = levelSelectList->getFirstSelectedItem()->getID();
+ if(id >= mFilesList.size())
+ return true;
+ const std::string level = mFilesList[id];
+ if(mInGame)
+ {
+ getModeManager().requestSavedGame(level);
+ return true;
+ }
+ return launchSavedGame(level);
+}
+
+bool MenuModeLoad::launchSavedGame(const std::string& level)
+{
std::string nickname = ConfigManager::getSingleton().getGameValue(Config::NICKNAME, std::string(), false);
- if (!nickname.empty())
+ if(!nickname.empty())
ODFrameListener::getSingleton().getClientGameMap()->setLocalPlayerNick(nickname);
-
- tmpWin = getModeManager().getGui().getGuiSheet(Gui::loadSavedGameMenu)->getChild("LoadingText");
+ CEGUI::Window* tmpWin = getModeManager().getGui().getGuiSheet(Gui::loadSavedGameMenu)->getChild("LoadingText");
tmpWin->setText("Loading...");
tmpWin->show();
- CEGUI::ListboxItem* selItem = levelSelectList->getFirstSelectedItem();
- int id = selItem->getID();
-
- const std::string& level = mFilesList[id];
// In single player mode, we act as a server
if(!ODServer::getSingleton().startServer(nickname, level, ServerMode::ModeGameLoaded, false))
{
@@ -159,6 +207,7 @@ bool MenuModeLoad::launchSelectedButtonPressed(const CEGUI::EventArgs&)
+ ResourceManager::getSingleton().buildReplayFilename();
if(!ODClient::getSingleton().connect("localhost", port, timeout, replayFilename))
{
+ ODServer::getSingleton().stopServer();
OD_LOG_ERR("Could not connect to server for single player game !!!");
tmpWin = getModeManager().getGui().getGuiSheet(Gui::loadSavedGameMenu)->getChild("LoadingText");
tmpWin->setText("Error: Couldn't connect to local server!");
@@ -227,6 +276,7 @@ bool MenuModeLoad::updateDescription(const CEGUI::EventArgs&)
mapDescription = "invalid map";
descTxt->setText(mapDescription);
+ static_cast(descTxt->getChild("__auto_vscrollbar__"))->setScrollPosition(0);
return true;
}
diff --git a/source/modes/MenuModeLoad.h b/source/modes/MenuModeLoad.h
index 8b29f49c8..7d2c40f02 100755
--- a/source/modes/MenuModeLoad.h
+++ b/source/modes/MenuModeLoad.h
@@ -23,7 +23,8 @@
class MenuModeLoad: public AbstractApplicationMode
{
public:
- MenuModeLoad(ModeManager*);
+ MenuModeLoad(ModeManager*, bool inGame = false, const std::string& savedGame = {});
+ ~MenuModeLoad() override;
//! \brief Called when the game mode is activated
//! Used to call the corresponding Gui Sheet.
@@ -32,8 +33,15 @@ class MenuModeLoad: public AbstractApplicationMode
bool launchSelectedButtonPressed(const CEGUI::EventArgs&);
bool deleteSelectedButtonPressed(const CEGUI::EventArgs&);
bool updateDescription(const CEGUI::EventArgs&);
+ bool closeBrowser(const CEGUI::EventArgs& = {});
+ bool isOpenInGame() const { return mInGame && mOpen; }
private:
+ bool launchSavedGame(const std::string& level);
+ bool mInGame;
+ bool mOpen = false;
+ bool mWasPaused = false;
+ std::string mSavedGame;
std::vector mFilesList;
};
diff --git a/source/modes/MenuModeMain.cpp b/source/modes/MenuModeMain.cpp
index 99fdd8fec..afc1f2e3d 100755
--- a/source/modes/MenuModeMain.cpp
+++ b/source/modes/MenuModeMain.cpp
@@ -192,6 +192,21 @@ bool MenuModeMain::toggleSettings(const CEGUI::EventArgs&)
return true;
}
+bool MenuModeMain::goBack(const CEGUI::EventArgs&)
+{
+ CEGUI::Window* mainWin = getModeManager().getGui().getGuiSheet(Gui::mainMenu);
+ for(const std::string& name : {WINDOW_SKIRMISH, WINDOW_MULTIPLAYER, WINDOW_EDITOR})
+ {
+ CEGUI::Window* window = mainWin->getChild(name);
+ if(window->isVisible())
+ {
+ window->hide();
+ break;
+ }
+ }
+ return true;
+}
+
bool MenuModeMain::toggleSkirmishSubMenu(const CEGUI::EventArgs&)
{
CEGUI::Window* mainWin = getModeManager().getGui().getGuiSheet(Gui::mainMenu);
diff --git a/source/modes/MenuModeMain.h b/source/modes/MenuModeMain.h
index 85dbc1f49..09d847ab8 100755
--- a/source/modes/MenuModeMain.h
+++ b/source/modes/MenuModeMain.h
@@ -34,6 +34,8 @@ class MenuModeMain: public AbstractApplicationMode
//! Used to call the corresponding Gui Sheet.
void activate() final override;
+ bool goBack(const CEGUI::EventArgs& e = {}) override;
+
private:
//! \brief The Settings window
SettingsWindow mSettings;
diff --git a/source/modes/ModeManager.cpp b/source/modes/ModeManager.cpp
index 58a1e0a52..0c3a214f8 100755
--- a/source/modes/ModeManager.cpp
+++ b/source/modes/ModeManager.cpp
@@ -73,6 +73,12 @@ void ModeManager::requestPreviousMode()
mStoreCurrentModeAtChange = false;
}
+void ModeManager::requestSavedGame(const std::string& filename)
+{
+ mRequestedSavedGame = filename;
+ requestMode(MENU_LOAD_SAVEDGAME, false);
+}
+
void ModeManager::checkModeChange()
{
if (mRequestedMode == NONE)
@@ -128,7 +134,10 @@ void ModeManager::checkModeChange()
mCurrentApplicationMode = Utils::make_unique(this);
break;
case MENU_LOAD_SAVEDGAME:
- mCurrentApplicationMode = Utils::make_unique(this);
+ mCurrentApplicationMode = Utils::make_unique(this, false, mRequestedSavedGame);
+ mRequestedSavedGame.clear();
+ if(previousMode == GAME)
+ mPreviousModeTypes.clear();
break;
case MENU_MASTERSERVER_HOST:
mCurrentApplicationMode = Utils::make_unique(this, true);
diff --git a/source/modes/ModeManager.h b/source/modes/ModeManager.h
index c4af9873c..2e2895806 100755
--- a/source/modes/ModeManager.h
+++ b/source/modes/ModeManager.h
@@ -55,6 +55,7 @@ class ModeManager final : public AbstractModeManager
//! \brief Request to load the previous mode type.
void requestPreviousMode();
+ void requestSavedGame(const std::string& filename);
InputManager& getInputManager()
{ return mInputManager; }
@@ -83,6 +84,7 @@ class ModeManager final : public AbstractModeManager
//! \brief Tells whether the current mode should be kept in history
//! when changing from the current mode.
bool mStoreCurrentModeAtChange;
+ std::string mRequestedSavedGame;
//! \brief Actually change the mode if needed
void checkModeChange();
diff --git a/source/network/ODClient.cpp b/source/network/ODClient.cpp
index 50d07e3be..fa304c0d7 100755
--- a/source/network/ODClient.cpp
+++ b/source/network/ODClient.cpp
@@ -234,9 +234,39 @@ bool ODClient::processMessage(ServerNotificationType cmd, ODPacket& packetReceiv
{
case ServerMode::ModeGameSinglePlayer:
case ServerMode::ModeGameMultiPlayer:
- case ServerMode::ModeGameLoaded:
frameListener->getModeManager()->requestMode(AbstractModeManager::MENU_CONFIGURE_SEATS);
break;
+ case ServerMode::ModeGameLoaded:
+ {
+ // A saved skirmish already fixes its human side, faction and team.
+ bool fixedSeats = getSource() != ODSource::file;
+ uint32_t humanSeats = 0;
+ for(Seat* seat : gameMap->getSeats())
+ {
+ if(seat->isRogueSeat())
+ continue;
+ if(seat->getPlayerType() == Seat::PLAYER_TYPE_HUMAN)
+ ++humanSeats;
+ else if(seat->getPlayerType() != Seat::PLAYER_TYPE_AI &&
+ seat->getPlayerType() != Seat::PLAYER_TYPE_INACTIVE)
+ fixedSeats = false;
+ if(seat->getAvailableTeamIds().size() != 1 ||
+ seat->getFaction() == Seat::PLAYER_FACTION_CHOICE)
+ fixedSeats = false;
+ }
+ if(fixedSeats && humanSeats == 1)
+ {
+ ODPacket ready;
+ ready << ClientNotificationType::readyForSeatConfiguration;
+ send(ready);
+ ODPacket start;
+ start << ClientNotificationType::seatConfigurationSet;
+ send(start);
+ }
+ else
+ frameListener->getModeManager()->requestMode(AbstractModeManager::MENU_CONFIGURE_SEATS);
+ break;
+ }
case ServerMode::ModeEditor:
break;
default:
@@ -273,6 +303,8 @@ bool ODClient::processMessage(ServerNotificationType cmd, ODPacket& packetReceiv
case ServerNotificationType::addPlayers:
{
+ if(frameListener->getModeManager()->getCurrentModeType() == ModeManager::MENU_LOAD_SAVEDGAME)
+ break;
if(frameListener->getModeManager()->getCurrentModeType() != ModeManager::ModeType::MENU_CONFIGURE_SEATS)
{
OD_LOG_ERR("Wrong mode " + Helper::toString(frameListener->getModeManager()->getCurrentModeType()));