Browse Source

Merge pull request #5357 from GeorgeK1ng/main_menu_1.7

[1.7] Main menu improvements
Ivan Savenko 7 months ago
parent
commit
eba3a26fca

+ 20 - 3
client/lobby/CSelectionBase.cpp

@@ -94,13 +94,30 @@ CSelectionBase::CSelectionBase(ESelectionScreen type)
 	if(screenType == ESelectionScreen::campaignList)
 	{
 		setBackground(ImagePath::builtin("CamCust.bmp"));
+		pos = background->center();
 	}
 	else
 	{
-		const JsonVector & bgNames = CMainMenuConfig::get().getConfig()["game-select"].Vector();
-		setBackground(ImagePath::fromJson(*RandomGeneratorUtil::nextItem(bgNames, CRandomGenerator::getDefault())));
+
+		const JsonNode& gameSelectConfig = CMainMenuConfig::get().getConfig()["scenario-selection"];
+		const JsonVector* bgNames = nullptr;
+
+		if (!gameSelectConfig.isStruct()) 
+			bgNames = &CMainMenuConfig::get().getConfig()["game-select"].Vector();  // Fallback for 1.6 mods
+		else
+			bgNames = &gameSelectConfig["background"].Vector();
+		
+
+		setBackground(ImagePath::fromJson(*RandomGeneratorUtil::nextItem(*bgNames, CRandomGenerator::getDefault())));
+		pos = background->center();
+
+		for (const JsonNode& node : gameSelectConfig["images"].Vector())
+		{
+			auto image = std::make_shared<CPicture>(ImagePath::fromJson(*RandomGeneratorUtil::nextItem(node["name"].Vector(), CRandomGenerator::getDefault())), Point(node["x"].Integer(), node["y"].Integer()));
+			images.push_back(image);
+		}
 	}
-	pos = background->center();
+	
 	card = std::make_shared<InfoCard>();
 	buttonBack = std::make_shared<CButton>(Point(581, 535), AnimationPath::builtin("SCNRBACK.DEF"), LIBRARY->generaltexth->zelp[105], [this](){ close();}, EShortcut::GLOBAL_CANCEL);
 }

+ 2 - 0
client/lobby/CSelectionBase.h

@@ -71,6 +71,8 @@ public:
 	std::shared_ptr<CButton> buttonBack;
 	std::shared_ptr<CButton> buttonSimturns;
 
+	std::vector<std::shared_ptr<CPicture>> images;
+
 	std::shared_ptr<SelectionTab> tabSel;
 	std::shared_ptr<OptionsTab> tabOpt;
 	std::shared_ptr<TurnOptionsTab> tabTurnOptions;

+ 51 - 37
client/mainmenu/CMainMenu.cpp

@@ -77,7 +77,7 @@ CMenuScreen::CMenuScreen(const JsonNode & configNode)
 	OBJECT_CONSTRUCTION;
 
 	const auto& bgConfig = config["background"];
-	if (bgConfig.isVector() && !bgConfig.Vector().empty())
+	if (bgConfig.isVector())
 		background = std::make_shared<CPicture>(ImagePath::fromJson(*RandomGeneratorUtil::nextItem(bgConfig.Vector(), CRandomGenerator::getDefault())));
 
 	if (bgConfig.isString())
@@ -88,6 +88,12 @@ CMenuScreen::CMenuScreen(const JsonNode & configNode)
 
 	pos = background->center();
 
+	for (const JsonNode& node : config["images"].Vector())
+	{
+		auto image = std::make_shared<CPicture>(ImagePath::fromJson(*RandomGeneratorUtil::nextItem(node["name"].Vector(), CRandomGenerator::getDefault())), Point(node["x"].Integer(), node["y"].Integer()));
+		images.push_back(image);
+	}
+
 	if(!config["video"].isNull())
 	{
 		Point videoPosition(config["video"]["x"].Integer(), config["video"]["y"].Integer());
@@ -97,9 +103,6 @@ CMenuScreen::CMenuScreen(const JsonNode & configNode)
 	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");
 
@@ -239,14 +242,13 @@ std::shared_ptr<CButton> CMenuEntry::createButton(CMenuScreen * parent, const Js
 	EShortcut shortcut = ENGINE->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;
 }
 
@@ -259,32 +261,38 @@ CMenuEntry::CMenuEntry(CMenuScreen * parent, const JsonNode & config)
 	for(const JsonNode & node : config["images"].Vector())
 		images.push_back(CMainMenu::createPicture(node));
 
-	for (const JsonNode& node : config["buttons"].Vector()) {
+	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") {
+		if (commandParts.first == "campaigns")
+		{
 			const auto& campaign = CMainMenuConfig::get().getCampaigns()[commandParts.second];
 
-			if (!campaign.isStruct()) {
+			if (!campaign.isStruct())
+			{
 				logGlobal->warn("Campaign set %s not found", commandParts.second);
 				continue;
 			}
 
 			bool fileExists = false;
-			for (const auto& item : campaign["items"].Vector()) {
+			for (const auto& item : campaign["items"].Vector())
+			{
 				std::string filename = item["file"].String();
 
-				if (CResourceHandler::get()->existsResource(ResourcePath(filename, EResType::CAMPAIGN))) {
+				if (CResourceHandler::get()->existsResource(ResourcePath(filename, EResType::CAMPAIGN)))
+				{
 					fileExists = true;
 					break; 
 				}
 			}
 
-			if (!fileExists) {
+			if (!fileExists)
+			{
 				logGlobal->warn("No valid files found for campaign set %s", commandParts.second);
 				continue;
 			}
@@ -300,8 +308,10 @@ 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);
+	if (!config["scenario-selection"].isStruct())
+		// Fallback for 1.6 mods
+		if (config["game-select"].Vector().empty())
+			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()
@@ -477,15 +487,6 @@ CMultiMode::CMultiMode(ESelectionScreen ScreenType)
 	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(LIBRARY->generaltexth->zelp[263].second);
 
@@ -704,25 +705,38 @@ CLoadingScreen::CLoadingScreen(ImagePath background)
 	ENGINE->music().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()));
+	const auto& backgroundConfig = conf["background"];
+	if (!backgroundConfig.Vector().empty())
+		backimg = std::make_shared<CPicture>(ImagePath::fromJson(*RandomGeneratorUtil::nextItem(backgroundConfig.Vector(), CRandomGenerator::getDefault())));
 
-	if (nameConfig.isString())
-		animationPath = AnimationPath::fromJson(nameConfig);
+	for (const JsonNode& node : conf["images"].Vector())
+	{
+		auto image = std::make_shared<CPicture>(ImagePath::fromJson(*RandomGeneratorUtil::nextItem(node["name"].Vector(), CRandomGenerator::getDefault())), Point(node["x"].Integer(), node["y"].Integer()));
+		images.push_back(image);
+	}
 
-	if (conf.isStruct())
+	const auto& loadframeConfig = conf["loadframe"];
+	if (loadframeConfig.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)
+		loadFrame = std::make_shared<CPicture>(
+			ImagePath::fromJson(*RandomGeneratorUtil::nextItem(loadframeConfig["name"].Vector(), CRandomGenerator::getDefault())),
+			loadframeConfig["x"].Integer(),
+			loadframeConfig["y"].Integer()
+			);
+	}
+
+	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>(animationPath, i, 0, posx + i * blockSize, posy));
+			progressBlocks.push_back(std::make_shared<CAnimImage>(loadbarPath, i, 0, posx + i * blockSize, posy));
 			progressBlocks.back()->deactivate();
 			progressBlocks.back()->visible = false;
 		}

+ 4 - 1
client/mainmenu/CMainMenu.h

@@ -189,8 +189,11 @@ public:
 
 class CLoadingScreen : virtual public CWindowObject, virtual public Load::Progress
 {
+	std::shared_ptr<CPicture> backimg;
+	std::vector<std::shared_ptr<CPicture>> images;
+	std::shared_ptr<CPicture> loadFrame;
 	std::vector<std::shared_ptr<CAnimImage>> progressBlocks;
-	
+
 	ImagePath getBackground();
 
 public:	

+ 43 - 9
config/mainmenu.json

@@ -1,25 +1,59 @@
 {
 	//images used in game selection screen
-	"game-select" : ["gamselb0", "gamselb1"],
-
-	//Loading screen background and progress bar
+	"scenario-selection" :
+	{
+		"background" : ["gamselb0", "gamselb1"],
+		
+		// Additional images
+		//"images": 
+		//[
+		//	{ "x": 10, "y": 0, "name": ["img1", "img2" ]},
+		//	{ "x": 10, "y": 140, "name": ["pic1", "pic2" ]} 
+		//]
+	},
+	
 	"loading" :
 	{
 		"background" : ["loadbar"],
-		"x": 395, "y": 548, "size": 18, "amount": 20,
-		"name": "loadprog"
-	},
+		
+		"loadbar" : {
+			"name": ["loadprog"],
+			"x": 395, "y": 548, "size": 18, "amount": 20,
+		},
+
+		//"loadframe" : {
+		//	"name": ["loadframe0", "loadframe1", "loadframe2", "loadframe3"],
+		//	"x": 390, "y": 543
+		//},
 
+		// Additional images
+		//"images": 
+		//[
+		//	{ "x": 10, "y": 0, "name": ["img1", "img2" ]},
+		//	{ "x": 10, "y": 140, "name": ["pic1", "pic2" ]} 
+		//]
+		
+	},
+	
 	//Multiplayer selection image
 	"multiplayer" : ["mumap"],
-	
+
 	//Main menu window, consists of several sub-menus aka items
 	"window":
 	{
-		"background" : "gamselbk",
 		//"scalable" : true, //background will be scaled to screen size
+		
+		"background" : ["gamselbk"],
+
 		//"video" :    {"x": 8, "y": 105, "name":"CREDITS.SMK" },//Floating WoG logo. Disabled due to different position in various versions of H3.
-		//"images" : [],//Optional, contains any additional images in the same format as video
+		
+		// Additional images
+		//"images": 
+		//[
+		//	{ "x": 10, "y": 0, "name": ["img1", "img2" ]},
+		//	{ "x": 10, "y": 140, "name": ["pic1", "pic2" ]} 
+		//]
+
 		"items" : 
 		[
 			{