فهرست منبع

Merge pull request #2530 from Nordsoft91/tutorial

Support tutorial
Nordsoft91 2 سال پیش
والد
کامیت
4a6deedd5e

+ 0 - 1
Mods/vcmi/config/vcmi/english.json

@@ -37,7 +37,6 @@
 	"vcmi.radialWheel.moveUnit" : "Move creatures to another army",
 	"vcmi.radialWheel.splitUnit" : "Split creature to another slot",
 
-	"vcmi.mainMenu.tutorialNotImplemented" : "Sorry, tutorial is not implemented yet\n",
 	"vcmi.mainMenu.highscoresNotImplemented" : "Sorry, high scores menu is not implemented yet\n",
 	"vcmi.mainMenu.serverConnecting" : "Connecting...",
 	"vcmi.mainMenu.serverAddressEnter" : "Enter address:",

+ 0 - 1
Mods/vcmi/config/vcmi/french.json

@@ -30,7 +30,6 @@
 	"vcmi.capitalColors.6" : "Turquoise",
 	"vcmi.capitalColors.7" : "Rose",
 
-	"vcmi.mainMenu.tutorialNotImplemented" : "Désolé, le didacticiel n'est pas encore implémenté\n",
 	"vcmi.mainMenu.highscoresNotImplemented" : "Désolé, le menu des meilleurs scores n'est pas encore implémenté\n",
 	"vcmi.mainMenu.serverConnecting" : "Connexion...",
 	"vcmi.mainMenu.serverAddressEnter" : "Entrez l'adresse :",

+ 0 - 1
Mods/vcmi/config/vcmi/german.json

@@ -37,7 +37,6 @@
 	"vcmi.radialWheel.moveUnit" : "Verschieben der Kreatur in andere Armee",
 	"vcmi.radialWheel.splitUnit" : "Aufsplitten der Kreatur in anderen Slot",
 
-	"vcmi.mainMenu.tutorialNotImplemented" : "Das Tutorial ist aktuell noch nicht implementiert\n",
 	"vcmi.mainMenu.highscoresNotImplemented" : "Die Highscores sind aktuell noch nicht implementiert\n",
 	"vcmi.mainMenu.serverConnecting" : "Verbinde...",
 	"vcmi.mainMenu.serverAddressEnter" : "Addresse eingeben:",

+ 0 - 1
Mods/vcmi/config/vcmi/polish.json

@@ -37,7 +37,6 @@
 	"vcmi.radialWheel.moveUnit" : "Przenieś stworzenia do innej armii",
 	"vcmi.radialWheel.splitUnit" : "Podziel jednostkę do wybranego miejsca",
 
-	"vcmi.mainMenu.tutorialNotImplemented" : "Przepraszamy, trening nie został jeszcze zaimplementowany\n",
 	"vcmi.mainMenu.highscoresNotImplemented" : "Przepraszamy, najlepsze wyniki nie zostały jeszcze zaimplementowane\n",
 	"vcmi.mainMenu.serverConnecting" : "Łączenie...",
 	"vcmi.mainMenu.serverAddressEnter" : "Wprowadź adres:",

+ 0 - 1
Mods/vcmi/config/vcmi/ukrainian.json

@@ -37,7 +37,6 @@
 	"vcmi.radialWheel.moveUnit" : "Перемістити істоту до іншої армії",
 	"vcmi.radialWheel.splitUnit" : "Розділити істоту в інший слот",
 
-	"vcmi.mainMenu.tutorialNotImplemented" : "Вибачте, навчання ще не реалізовано\n",
 	"vcmi.mainMenu.highscoresNotImplemented" : "Вибачте, таблицю рекордів ще не реалізовано\n",
 	"vcmi.mainMenu.serverConnecting" : "Підключення...",
 	"vcmi.mainMenu.serverAddressEnter" : "Вкажіть адресу:",

+ 8 - 1
client/CServerHandler.cpp

@@ -143,6 +143,7 @@ void CServerHandler::resetStateForLobby(const StartInfo::EMode mode, const std::
 {
 	hostClientId = -1;
 	state = EClientState::NONE;
+	mapToStart = nullptr;
 	th = std::make_unique<CStopWatch>();
 	packsForLobbyScreen.clear();
 	c.reset();
@@ -399,6 +400,7 @@ void CServerHandler::sendClientDisconnecting()
 		return;
 
 	state = EClientState::DISCONNECTING;
+	mapToStart = nullptr;
 	LobbyClientDisconnected lcd;
 	lcd.clientId = c->connectionID;
 	logNetwork->info("Connection has been requested to be closed.");
@@ -565,6 +567,11 @@ void CServerHandler::sendStartGame(bool allowOnlyAI) const
 	c->disableStackSendingByID();
 }
 
+void CServerHandler::startMapAfterConnection(std::shared_ptr<CMapInfo> to)
+{
+	mapToStart = to;
+}
+
 void CServerHandler::startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameState)
 {
 	if(CMM)
@@ -694,7 +701,7 @@ int CServerHandler::howManyPlayerInterfaces()
 
 ui8 CServerHandler::getLoadMode()
 {
-	if(state == EClientState::GAMEPLAY)
+	if(loadMode != ELoadMode::TUTORIAL && state == EClientState::GAMEPLAY)
 	{
 		if(si->campState)
 			return ELoadMode::CAMPAIGN;

+ 5 - 0
client/CServerHandler.h

@@ -74,10 +74,14 @@ public:
 /// structure to handle running server and connecting to it
 class CServerHandler : public IServerAPI, public LobbyInfo
 {
+	friend class ApplyOnLobbyHandlerNetPackVisitor;
+	
 	std::shared_ptr<CApplier<CBaseForLobbyApply>> applier;
 
 	std::shared_ptr<boost::recursive_mutex> mx;
 	std::list<CPackForLobby *> packsForLobbyScreen; //protected by mx
+	
+	std::shared_ptr<CMapInfo> mapToStart;
 
 	std::vector<std::string> myNames;
 
@@ -148,6 +152,7 @@ public:
 	void sendRestartGame() const override;
 	void sendStartGame(bool allowOnlyAI = false) const override;
 
+	void startMapAfterConnection(std::shared_ptr<CMapInfo> to);
 	void startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameState = nullptr);
 	void endGameplay(bool closeConnection = true, bool restart = false);
 	void startCampaignScenario(std::shared_ptr<CampaignState> cs = {});

+ 8 - 1
client/NetPacksLobbyClient.cpp

@@ -38,7 +38,9 @@ void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyClientConnected(LobbyClientCon
 	if(pack.uuid == handler.c->uuid)
 	{
 		handler.c->connectionID = pack.clientId;
-		if(!settings["session"]["headless"].Bool())
+		if(handler.mapToStart)
+			handler.setMapInfo(handler.mapToStart);
+		else if(!settings["session"]["headless"].Bool())
 			GH.windows().createAndPushWindow<CLobbyScreen>(static_cast<ESelectionScreen>(handler.screenType));
 		handler.state = EClientState::LOBBY;
 	}
@@ -136,6 +138,11 @@ void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyUpdateState(LobbyUpdateState &
 {
 	pack.hostChanged = pack.state.hostClientId != handler.hostClientId;
 	static_cast<LobbyState &>(handler) = pack.state;
+	if(handler.mapToStart && handler.mi)
+	{
+		handler.startMapAfterConnection(nullptr);
+		handler.sendStartGame();
+	}
 }
 
 void ApplyOnLobbyScreenNetPackVisitor::visitLobbyUpdateState(LobbyUpdateState & pack)

+ 13 - 4
client/lobby/SelectionTab.cpp

@@ -231,9 +231,13 @@ void SelectionTab::toggleMode()
 		switch(tabType)
 		{
 		case ESelectionScreen::newGame:
-			inputName->disable();
-			parseMaps(getFiles("Maps/", EResType::MAP));
-			break;
+			{
+				inputName->disable();
+				auto files = getFiles("Maps/", EResType::MAP);
+				files.erase(ResourceID("Maps/Tutorial.tut", EResType::MAP));
+				parseMaps(files);
+				break;
+			}
 
 		case ESelectionScreen::loadGame:
 			inputName->disable();
@@ -742,16 +746,21 @@ void SelectionTab::parseSaves(const std::unordered_set<ResourceID> & files)
 			// Filter out other game modes
 			bool isCampaign = mapInfo->scenarioOptionsOfSave->mode == StartInfo::CAMPAIGN;
 			bool isMultiplayer = mapInfo->amountOfHumanPlayersInSave > 1;
+			bool isTutorial = boost::to_upper_copy(mapInfo->scenarioOptionsOfSave->mapname) == "MAPS/TUTORIAL";
 			switch(CSH->getLoadMode())
 			{
 			case ELoadMode::SINGLE:
-				if(isMultiplayer || isCampaign)
+				if(isMultiplayer || isCampaign || isTutorial)
 					mapInfo->mapHeader.reset();
 				break;
 			case ELoadMode::CAMPAIGN:
 				if(!isCampaign)
 					mapInfo->mapHeader.reset();
 				break;
+			case ELoadMode::TUTORIAL:
+				if(!isTutorial)
+					mapInfo->mapHeader.reset();
+				break;
 			default:
 				if(!isMultiplayer)
 					mapInfo->mapHeader.reset();

+ 18 - 2
client/mainmenu/CMainMenu.cpp

@@ -47,6 +47,7 @@
 #include "../../lib/serializer/CTypeList.h"
 #include "../../lib/filesystem/Filesystem.h"
 #include "../../lib/filesystem/CCompressedStream.h"
+#include "../../lib/mapping/CMapInfo.h"
 #include "../../lib/VCMIDirs.h"
 #include "../../lib/CStopWatch.h"
 #include "../../lib/NetPacksLobby.h"
@@ -183,7 +184,7 @@ static std::function<void()> genCommand(CMenuScreen * menu, std::vector<std::str
 				case 2:
 					return std::bind(CMainMenu::openLobby, ESelectionScreen::campaignList, true, nullptr, ELoadMode::NONE);
 				case 3:
-					return std::bind(CInfoWindow::showInfoDialog, CGI->generaltexth->translate("vcmi.mainMenu.tutorialNotImplemented"), std::vector<std::shared_ptr<CComponent>>(), PlayerColor(1));
+					return std::bind(CMainMenu::startTutorial);
 				}
 				break;
 			}
@@ -198,7 +199,7 @@ static std::function<void()> genCommand(CMenuScreen * menu, std::vector<std::str
 				case 2:
 					return std::bind(CMainMenu::openLobby, ESelectionScreen::loadGame, true, nullptr, ELoadMode::CAMPAIGN);
 				case 3:
-					return std::bind(CInfoWindow::showInfoDialog, CGI->generaltexth->translate("vcmi.mainMenu.tutorialNotImplemented"), std::vector<std::shared_ptr<CComponent>>(), PlayerColor(1));
+					return std::bind(CMainMenu::openLobby, ESelectionScreen::loadGame, true, nullptr, ELoadMode::TUTORIAL);
 				}
 			}
 			break;
@@ -371,6 +372,21 @@ void CMainMenu::openCampaignScreen(std::string name)
 	logGlobal->error("Unknown campaign set: %s", name);
 }
 
+void CMainMenu::startTutorial()
+{
+	ResourceID 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());
+	CSH->startMapAfterConnection(mapInfo);
+	CMainMenu::openLobby(ESelectionScreen::newGame, true, nullptr, ELoadMode::NONE);
+}
+
 std::shared_ptr<CMainMenu> CMainMenu::create()
 {
 	if(!CMM)

+ 2 - 1
client/mainmenu/CMainMenu.h

@@ -34,7 +34,7 @@ enum ESelectionScreen : ui8 {
 
 enum ELoadMode : ui8
 {
-	NONE = 0, SINGLE, MULTI, CAMPAIGN
+	NONE = 0, SINGLE, MULTI, CAMPAIGN, TUTORIAL
 };
 
 /// The main menu screens listed in the EState enum
@@ -148,6 +148,7 @@ public:
 	static void openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> * names, ELoadMode loadMode);
 	static void openCampaignLobby(const std::string & campaignFileName);
 	static void openCampaignLobby(std::shared_ptr<CampaignState> campaign);
+	static void startTutorial();
 	void openCampaignScreen(std::string name);
 
 	static std::shared_ptr<CMainMenu> create();

+ 1 - 0
lib/filesystem/ResourceID.cpp

@@ -132,6 +132,7 @@ EResType::Type EResTypeHelper::getTypeFromExtension(std::string extension)
 		{".MSG",   EResType::MASK},
 		{".H3C",   EResType::CAMPAIGN},
 		{".H3M",   EResType::MAP},
+		{".TUT",   EResType::MAP},
 		{".FNT",   EResType::BMP_FONT},
 		{".TTF",   EResType::TTF_FONT},
 		{".BMP",   EResType::IMAGE},