mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-10 00:43:59 +02:00
Merge pull request #4900 from Laserlicht/chronicles_menu
Chronicles menu
This commit is contained in:
commit
90304e5c8a
@ -875,6 +875,9 @@ void SelectionTab::parseSaves(const std::unordered_set<ResourcePath> & files)
|
|||||||
|
|
||||||
void SelectionTab::parseCampaigns(const std::unordered_set<ResourcePath> & files)
|
void SelectionTab::parseCampaigns(const std::unordered_set<ResourcePath> & files)
|
||||||
{
|
{
|
||||||
|
auto campaignSets = JsonNode(JsonPath::builtin("config/campaignSets.json"));
|
||||||
|
auto mainmenu = JsonNode(JsonPath::builtin("config/mainmenu.json"));
|
||||||
|
|
||||||
allItems.reserve(files.size());
|
allItems.reserve(files.size());
|
||||||
for(auto & file : files)
|
for(auto & file : files)
|
||||||
{
|
{
|
||||||
@ -882,8 +885,28 @@ void SelectionTab::parseCampaigns(const std::unordered_set<ResourcePath> & files
|
|||||||
info->fileURI = file.getOriginalName();
|
info->fileURI = file.getOriginalName();
|
||||||
info->campaignInit();
|
info->campaignInit();
|
||||||
info->name = info->getNameForList();
|
info->name = info->getNameForList();
|
||||||
|
|
||||||
if(info->campaign)
|
if(info->campaign)
|
||||||
allItems.push_back(info);
|
{
|
||||||
|
// skip campaigns organized in sets
|
||||||
|
std::string foundInSet = "";
|
||||||
|
for (auto const & set : campaignSets.Struct())
|
||||||
|
for (auto const & item : set.second["items"].Vector())
|
||||||
|
if(file.getName() == ResourcePath(item["file"].String()).getName())
|
||||||
|
foundInSet = set.first;
|
||||||
|
|
||||||
|
// set has to be used in main menu
|
||||||
|
bool setInMainmenu = false;
|
||||||
|
if(!foundInSet.empty())
|
||||||
|
for (auto const & item : mainmenu["window"]["items"].Vector())
|
||||||
|
if(item["name"].String() == "campaign")
|
||||||
|
for (auto const & button : item["buttons"].Vector())
|
||||||
|
if(boost::algorithm::ends_with(boost::algorithm::to_lower_copy(button["command"].String()), boost::algorithm::to_lower_copy(foundInSet)))
|
||||||
|
setInMainmenu = true;
|
||||||
|
|
||||||
|
if(!setInMainmenu)
|
||||||
|
allItems.push_back(info);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +67,8 @@ CCampaignScreen::CCampaignScreen(const JsonNode & config, std::string name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for(const JsonNode & node : config[name]["items"].Vector())
|
for(const JsonNode & node : config[name]["items"].Vector())
|
||||||
campButtons.push_back(std::make_shared<CCampaignButton>(node, config, campaignSet));
|
if(CResourceHandler::get()->existsResource(ResourcePath(node["file"].String(), EResType::CAMPAIGN)))
|
||||||
|
campButtons.push_back(std::make_shared<CCampaignButton>(node, config, campaignSet));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CCampaignScreen::activate()
|
void CCampaignScreen::activate()
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
#include "../widgets/VideoWidget.h"
|
#include "../widgets/VideoWidget.h"
|
||||||
#include "../windows/InfoWindows.h"
|
#include "../windows/InfoWindows.h"
|
||||||
#include "../CServerHandler.h"
|
#include "../CServerHandler.h"
|
||||||
|
#include "../render/AssetGenerator.h"
|
||||||
|
|
||||||
#include "../CGameInfo.h"
|
#include "../CGameInfo.h"
|
||||||
#include "../CPlayerInterface.h"
|
#include "../CPlayerInterface.h"
|
||||||
@ -403,6 +404,9 @@ void CMainMenu::openCampaignScreen(std::string name)
|
|||||||
{
|
{
|
||||||
auto const & config = CMainMenuConfig::get().getCampaigns();
|
auto const & config = CMainMenuConfig::get().getCampaigns();
|
||||||
|
|
||||||
|
AssetGenerator::createCampaignBackground();
|
||||||
|
AssetGenerator::createChroniclesCampaignImages();
|
||||||
|
|
||||||
if(!vstd::contains(config.Struct(), name))
|
if(!vstd::contains(config.Struct(), name))
|
||||||
{
|
{
|
||||||
logGlobal->error("Unknown campaign set: %s", name);
|
logGlobal->error("Unknown campaign set: %s", name);
|
||||||
@ -413,7 +417,9 @@ void CMainMenu::openCampaignScreen(std::string name)
|
|||||||
for (auto const & entry : config[name]["items"].Vector())
|
for (auto const & entry : config[name]["items"].Vector())
|
||||||
{
|
{
|
||||||
ResourcePath resourceID(entry["file"].String(), EResType::CAMPAIGN);
|
ResourcePath resourceID(entry["file"].String(), EResType::CAMPAIGN);
|
||||||
if (!CResourceHandler::get()->existsResource(resourceID))
|
if(entry["optional"].Bool())
|
||||||
|
continue;
|
||||||
|
if(!CResourceHandler::get()->existsResource(resourceID))
|
||||||
campaignsFound = false;
|
campaignsFound = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +30,8 @@ void AssetGenerator::generateAll()
|
|||||||
for (int i = 0; i < PlayerColor::PLAYER_LIMIT_I; ++i)
|
for (int i = 0; i < PlayerColor::PLAYER_LIMIT_I; ++i)
|
||||||
createPlayerColoredBackground(PlayerColor(i));
|
createPlayerColoredBackground(PlayerColor(i));
|
||||||
createCombatUnitNumberWindow();
|
createCombatUnitNumberWindow();
|
||||||
|
createCampaignBackground();
|
||||||
|
createChroniclesCampaignImages();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetGenerator::createAdventureOptionsCleanBackground()
|
void AssetGenerator::createAdventureOptionsCleanBackground()
|
||||||
@ -206,3 +208,121 @@ void AssetGenerator::createCombatUnitNumberWindow()
|
|||||||
texture->adjustPalette(shifterNeutral, ignoredMask);
|
texture->adjustPalette(shifterNeutral, ignoredMask);
|
||||||
texture->exportBitmap(*CResourceHandler::get("local")->getResourceName(savePathNeutral));
|
texture->exportBitmap(*CResourceHandler::get("local")->getResourceName(savePathNeutral));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AssetGenerator::createCampaignBackground()
|
||||||
|
{
|
||||||
|
std::string filename = "data/CampaignBackground8.png";
|
||||||
|
|
||||||
|
if(CResourceHandler::get()->existsResource(ResourcePath(filename))) // overridden by mod, no generation
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(!CResourceHandler::get("local")->createResource(filename))
|
||||||
|
return;
|
||||||
|
ResourcePath savePath(filename, EResType::IMAGE);
|
||||||
|
|
||||||
|
auto locator = ImageLocator(ImagePath::builtin("CAMPBACK"));
|
||||||
|
locator.scalingFactor = 1;
|
||||||
|
|
||||||
|
std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator, EImageBlitMode::OPAQUE);
|
||||||
|
Canvas canvas = Canvas(Point(800, 600), CanvasScalingPolicy::IGNORE);
|
||||||
|
|
||||||
|
canvas.draw(img, Point(0, 0), Rect(0, 0, 800, 600));
|
||||||
|
|
||||||
|
// left image
|
||||||
|
canvas.draw(img, Point(220, 73), Rect(290, 73, 141, 115));
|
||||||
|
canvas.draw(img, Point(37, 70), Rect(87, 70, 207, 120));
|
||||||
|
|
||||||
|
// right image
|
||||||
|
canvas.draw(img, Point(513, 67), Rect(463, 67, 71, 126));
|
||||||
|
canvas.draw(img, Point(586, 71), Rect(536, 71, 207, 117));
|
||||||
|
|
||||||
|
// middle image
|
||||||
|
canvas.draw(img, Point(306, 68), Rect(86, 68, 209, 122));
|
||||||
|
|
||||||
|
// disabled fields
|
||||||
|
canvas.draw(img, Point(40, 72), Rect(313, 74, 197, 114));
|
||||||
|
canvas.draw(img, Point(310, 72), Rect(313, 74, 197, 114));
|
||||||
|
canvas.draw(img, Point(590, 72), Rect(313, 74, 197, 114));
|
||||||
|
canvas.draw(img, Point(43, 245), Rect(313, 74, 197, 114));
|
||||||
|
canvas.draw(img, Point(313, 244), Rect(313, 74, 197, 114));
|
||||||
|
canvas.draw(img, Point(586, 246), Rect(313, 74, 197, 114));
|
||||||
|
canvas.draw(img, Point(34, 417), Rect(313, 74, 197, 114));
|
||||||
|
canvas.draw(img, Point(404, 414), Rect(313, 74, 197, 114));
|
||||||
|
|
||||||
|
// skull
|
||||||
|
auto locatorSkull = ImageLocator(ImagePath::builtin("CAMPNOSC"));
|
||||||
|
locatorSkull.scalingFactor = 1;
|
||||||
|
std::shared_ptr<IImage> imgSkull = GH.renderHandler().loadImage(locatorSkull, EImageBlitMode::OPAQUE);
|
||||||
|
canvas.draw(imgSkull, Point(562, 509), Rect(178, 108, 43, 19));
|
||||||
|
|
||||||
|
std::shared_ptr<IImage> image = GH.renderHandler().createImage(canvas.getInternalSurface());
|
||||||
|
|
||||||
|
image->exportBitmap(*CResourceHandler::get("local")->getResourceName(savePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetGenerator::createChroniclesCampaignImages()
|
||||||
|
{
|
||||||
|
for(int i = 1; i < 9; i++)
|
||||||
|
{
|
||||||
|
std::string filename = "data/CampaignHc" + std::to_string(i) + "Image.png";
|
||||||
|
|
||||||
|
if(CResourceHandler::get()->existsResource(ResourcePath(filename))) // overridden by mod, no generation
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto imgPathBg = ImagePath::builtin("data/chronicles_" + std::to_string(i) + "/GamSelBk");
|
||||||
|
if(!CResourceHandler::get()->existsResource(imgPathBg)) // Chronicle episode not installed
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(!CResourceHandler::get("local")->createResource(filename))
|
||||||
|
continue;
|
||||||
|
ResourcePath savePath(filename, EResType::IMAGE);
|
||||||
|
|
||||||
|
auto locator = ImageLocator(imgPathBg);
|
||||||
|
locator.scalingFactor = 1;
|
||||||
|
|
||||||
|
std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator, EImageBlitMode::OPAQUE);
|
||||||
|
Canvas canvas = Canvas(Point(200, 116), CanvasScalingPolicy::IGNORE);
|
||||||
|
|
||||||
|
switch (i)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
canvas.draw(img, Point(0, 0), Rect(149, 144, 200, 116));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
canvas.draw(img, Point(0, 0), Rect(156, 150, 200, 116));
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
canvas.draw(img, Point(0, 0), Rect(171, 153, 200, 116));
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
canvas.draw(img, Point(0, 0), Rect(35, 358, 200, 116));
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
canvas.draw(img, Point(0, 0), Rect(216, 248, 200, 116));
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
canvas.draw(img, Point(0, 0), Rect(58, 234, 200, 116));
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
canvas.draw(img, Point(0, 0), Rect(184, 219, 200, 116));
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
canvas.draw(img, Point(0, 0), Rect(268, 210, 200, 116));
|
||||||
|
|
||||||
|
//skull
|
||||||
|
auto locatorSkull = ImageLocator(ImagePath::builtin("CampSP1"));
|
||||||
|
locatorSkull.scalingFactor = 1;
|
||||||
|
std::shared_ptr<IImage> imgSkull = GH.renderHandler().loadImage(locatorSkull, EImageBlitMode::OPAQUE);
|
||||||
|
canvas.draw(imgSkull, Point(162, 94), Rect(162, 94, 41, 22));
|
||||||
|
canvas.draw(img, Point(162, 94), Rect(424, 304, 14, 4));
|
||||||
|
canvas.draw(img, Point(162, 98), Rect(424, 308, 10, 4));
|
||||||
|
canvas.draw(img, Point(158, 102), Rect(424, 312, 10, 4));
|
||||||
|
canvas.draw(img, Point(154, 106), Rect(424, 316, 10, 4));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<IImage> image = GH.renderHandler().createImage(canvas.getInternalSurface());
|
||||||
|
|
||||||
|
image->exportBitmap(*CResourceHandler::get("local")->getResourceName(savePath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -21,4 +21,6 @@ public:
|
|||||||
static void createBigSpellBook();
|
static void createBigSpellBook();
|
||||||
static void createPlayerColoredBackground(const PlayerColor & player);
|
static void createPlayerColoredBackground(const PlayerColor & player);
|
||||||
static void createCombatUnitNumberWindow();
|
static void createCombatUnitNumberWindow();
|
||||||
|
static void createCampaignBackground();
|
||||||
|
static void createChroniclesCampaignImages();
|
||||||
};
|
};
|
||||||
|
@ -47,5 +47,21 @@
|
|||||||
{ "id": 6, "x":34, "y":417, "file":"DATA/FINAL", "image":"CAMPUA1", "video":"UNHOLY", "requires": [4] },
|
{ "id": 6, "x":34, "y":417, "file":"DATA/FINAL", "image":"CAMPUA1", "video":"UNHOLY", "requires": [4] },
|
||||||
{ "id": 7, "x":404, "y":414, "file":"DATA/SECRET", "image":"CAMPSP1", "video":"SPECTRE", "requires": [6] }
|
{ "id": 7, "x":404, "y":414, "file":"DATA/SECRET", "image":"CAMPSP1", "video":"SPECTRE", "requires": [6] }
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"chr":
|
||||||
|
{
|
||||||
|
"images" : [ {"x": 0, "y": 0, "name":"data/CampaignBackground8"} ],
|
||||||
|
"exitbutton" : {"x": 658, "y": 482, "name":"CMPSCAN" },
|
||||||
|
"items":
|
||||||
|
[
|
||||||
|
{ "id": 1, "x":40, "y":72, "file":"Maps/Chronicles/Hc1_Main", "image":"CampaignHc1Image", "video":"", "requires": [], "optional": true },
|
||||||
|
{ "id": 2, "x":310, "y":72, "file":"Maps/Chronicles/Hc2_Main", "image":"CampaignHc2Image", "video":"", "requires": [], "optional": true },
|
||||||
|
{ "id": 3, "x":590, "y":72, "file":"Maps/Chronicles/Hc3_Main", "image":"CampaignHc3Image", "video":"", "requires": [], "optional": true },
|
||||||
|
{ "id": 4, "x":43, "y":245, "file":"Maps/Chronicles/Hc4_Main", "image":"CampaignHc4Image", "video":"", "requires": [], "optional": true },
|
||||||
|
{ "id": 5, "x":313, "y":244, "file":"Maps/Chronicles/Hc5_Main", "image":"CampaignHc5Image", "video":"", "requires": [], "optional": true },
|
||||||
|
{ "id": 6, "x":586, "y":244, "file":"Maps/Chronicles/Hc6_Main", "image":"CampaignHc6Image", "video":"", "requires": [], "optional": true },
|
||||||
|
{ "id": 7, "x":34, "y":413, "file":"Maps/Chronicles/Hc7_Main", "image":"CampaignHc7Image", "video":"", "requires": [], "optional": true },
|
||||||
|
{ "id": 8, "x":404, "y":414, "file":"Maps/Chronicles/Hc8_Main", "image":"CampaignHc8Image", "video":"", "requires": [], "optional": true }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,10 @@
|
|||||||
VCMI is an open-source recreation of Heroes of Might & Magic III engine, giving it new and extended possibilities.
|
VCMI is an open-source recreation of Heroes of Might & Magic III engine, giving it new and extended possibilities.
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.3.0/Castle%20Siege.jpg?raw=true" alt="Vanilla town siege in extended window" style="height:120px;"/>
|
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.3.0/Castle%20Siege.jpg?raw=true" alt="Vanilla town siege in extended window" style="height:120px;"/>
|
||||||
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.3.0/Town%20Screen%20with%20Radial%20Menu.jpg?raw=true" alt="Vanilla town view with radial menu for touchscreen devices" style="height:120px;"/>
|
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.3.0/Town%20Screen%20with%20Radial%20Menu.jpg?raw=true" alt="Vanilla town view with radial menu for touchscreen devices" style="height:120px;"/>
|
||||||
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Big%20spellbook.jpg?raw=true" alt="Large Spellbook with German translation" style="height:120px;"/>
|
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Big%20spellbook.jpg?raw=true" alt="Large Spellbook with German translation" style="height:120px;"/>
|
||||||
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Quick%20Hero%20Select%20Bastion.jpg?raw=true" alt="New widget for Hero selection, featuring Pavillon Town" style="height:120px;"/>
|
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Quick%20Hero%20Select%20Bastion.jpg?raw=true" alt="New widget for Hero selection, featuring Pavillon Town" style="height:120px;"/>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
@ -37,10 +37,12 @@ Please see corresponding installation guide articles for details for your platfo
|
|||||||
- [Android](players/Installation_Android.md)
|
- [Android](players/Installation_Android.md)
|
||||||
- [iOS](players/Installation_iOS.md)
|
- [iOS](players/Installation_iOS.md)
|
||||||
|
|
||||||
|
See also installation guide for [Heroes Chronicles](players/Heroes_Chronicles.md).
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Antagarich%20Burning%20Battle.jpg?raw=true" alt="Forge Town in battle" style="height:120px;"/>
|
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Antagarich%20Burning%20Battle.jpg?raw=true" alt="Forge Town in battle" style="height:120px;"/>
|
||||||
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Town%20and%20Unit.jpg?raw=true" alt="Asylum town with new creature dialog" style="height:120px;"/>
|
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Town%20and%20Unit.jpg?raw=true" alt="Asylum town with new creature dialog" style="height:120px;"/>
|
||||||
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Siege.jpg?raw=true" alt="Ruins town siege" style="height:120px;"/>
|
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Siege.jpg?raw=true" alt="Ruins town siege" style="height:120px;"/>
|
||||||
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Editor.jpg?raw=true" alt="Map editor" style="height:120px;"/>
|
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Editor.jpg?raw=true" alt="Map editor" style="height:120px;"/>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
9
docs/players/Heroes_Chronicles.md
Normal file
9
docs/players/Heroes_Chronicles.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Heroes Chronicles
|
||||||
|
|
||||||
|
It also possible to play the Heroes Chronicles with VCMI. You still need a completly installed VCMI (with heroes 3 sod / complete files).
|
||||||
|
|
||||||
|
You also need Heroes Chronicles from [gog.com](https://www.gog.com/en/game/heroes_chronicles_all_chapters). You need to download the offline installer. CD installations are not supported yet.
|
||||||
|
|
||||||
|
You can use the "Install file" button in the launcher to select the downloaded exe files. This process can take a while (especially on mobile platforms) and need some temporary free space.
|
||||||
|
|
||||||
|
After that you can select Heroes Chronicles from Campaign selection menu (button or custom campaign).
|
Loading…
Reference in New Issue
Block a user