mirror of
https://github.com/vcmi/vcmi.git
synced 2025-05-29 22:57:49 +02:00
Merge branch 'develop' into patch-4map_overview_rework
This commit is contained in:
commit
ec8580b99e
@ -3,9 +3,8 @@
|
|||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
sudo apt-get install libboost-all-dev
|
sudo apt-get install libboost-all-dev \
|
||||||
sudo apt-get install libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev
|
libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev \
|
||||||
sudo apt-get install qt6-base-dev qt6-base-dev-tools qt6-tools-dev qt6-tools-dev-tools qt6-l10n-tools
|
qt6-base-dev qt6-base-dev-tools qt6-tools-dev qt6-tools-dev-tools qt6-l10n-tools \
|
||||||
sudo apt-get install ninja-build zlib1g-dev libavformat-dev libswscale-dev libtbb-dev libluajit-5.1-dev
|
ninja-build zlib1g-dev libavformat-dev libswscale-dev libtbb-dev libluajit-5.1-dev \
|
||||||
# Optional dependencies
|
libminizip-dev libfuzzylite-dev # Optional dependencies
|
||||||
sudo apt-get install libminizip-dev libfuzzylite-dev
|
|
||||||
|
@ -3,9 +3,8 @@
|
|||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
sudo apt-get install libboost-all-dev
|
sudo apt-get install libboost-all-dev \
|
||||||
sudo apt-get install libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev
|
libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev \
|
||||||
sudo apt-get install qtbase5-dev
|
qtbase5-dev \
|
||||||
sudo apt-get install ninja-build zlib1g-dev libavformat-dev libswscale-dev libtbb-dev libluajit-5.1-dev
|
ninja-build zlib1g-dev libavformat-dev libswscale-dev libtbb-dev libluajit-5.1-dev \
|
||||||
# Optional dependencies
|
libminizip-dev libfuzzylite-dev qttools5-dev # Optional dependencies
|
||||||
sudo apt-get install libminizip-dev libfuzzylite-dev qttools5-dev
|
|
||||||
|
@ -237,7 +237,7 @@ void CPlayerInterface::performAutosave()
|
|||||||
prefix = settings["general"]["savePrefix"].String();
|
prefix = settings["general"]["savePrefix"].String();
|
||||||
if(prefix.empty())
|
if(prefix.empty())
|
||||||
{
|
{
|
||||||
std::string name = cb->getMapHeader()->name;
|
std::string name = cb->getMapHeader()->name.toString();
|
||||||
int txtlen = TextOperations::getUnicodeCharactersCount(name);
|
int txtlen = TextOperations::getUnicodeCharactersCount(name);
|
||||||
|
|
||||||
TextOperations::trimRightUnicode(name, std::max(0, txtlen - 15));
|
TextOperations::trimRightUnicode(name, std::max(0, txtlen - 15));
|
||||||
@ -1716,7 +1716,7 @@ void CPlayerInterface::requestReturningToMainMenu(bool won)
|
|||||||
if(!ps->checkVanquished())
|
if(!ps->checkVanquished())
|
||||||
param.allDefeated = false;
|
param.allDefeated = false;
|
||||||
}
|
}
|
||||||
param.scenarioName = cb->getMapHeader()->name;
|
param.scenarioName = cb->getMapHeader()->name.toString();
|
||||||
param.playerName = cb->getStartInfo()->playerInfos.find(*cb->getPlayerID())->second.name;
|
param.playerName = cb->getStartInfo()->playerInfos.find(*cb->getPlayerID())->second.name;
|
||||||
HighScoreCalculation highScoreCalc;
|
HighScoreCalculation highScoreCalc;
|
||||||
highScoreCalc.parameters.push_back(param);
|
highScoreCalc.parameters.push_back(param);
|
||||||
|
@ -417,6 +417,9 @@ void CServerHandler::sendClientDisconnecting()
|
|||||||
logNetwork->info("Sent leaving signal to the server");
|
logNetwork->info("Sent leaving signal to the server");
|
||||||
}
|
}
|
||||||
sendLobbyPack(lcd);
|
sendLobbyPack(lcd);
|
||||||
|
|
||||||
|
c->close();
|
||||||
|
c.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CServerHandler::setCampaignState(std::shared_ptr<CampaignState> newCampaign)
|
void CServerHandler::setCampaignState(std::shared_ptr<CampaignState> newCampaign)
|
||||||
@ -701,7 +704,7 @@ void CServerHandler::startCampaignScenario(HighScoreParameter param, std::shared
|
|||||||
highScoreCalc->isCampaign = true;
|
highScoreCalc->isCampaign = true;
|
||||||
highScoreCalc->parameters.clear();
|
highScoreCalc->parameters.clear();
|
||||||
}
|
}
|
||||||
param.campaignName = cs->getName();
|
param.campaignName = cs->getNameTranslated();
|
||||||
highScoreCalc->parameters.push_back(param);
|
highScoreCalc->parameters.push_back(param);
|
||||||
|
|
||||||
GH.dispatchMainThread([ourCampaign, this]()
|
GH.dispatchMainThread([ourCampaign, this]()
|
||||||
|
@ -129,11 +129,10 @@ void CGuiHandler::renderFrame()
|
|||||||
|
|
||||||
CCS->curh->render();
|
CCS->curh->render();
|
||||||
|
|
||||||
SDL_RenderPresent(mainRenderer);
|
|
||||||
|
|
||||||
windows().onFrameRendered();
|
windows().onFrameRendered();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDL_RenderPresent(mainRenderer);
|
||||||
framerate().framerateDelay(); // holds a constant FPS
|
framerate().framerateDelay(); // holds a constant FPS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,11 +11,15 @@
|
|||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "FramerateManager.h"
|
#include "FramerateManager.h"
|
||||||
|
|
||||||
|
#include "../../lib/CConfigHandler.h"
|
||||||
|
#include <SDL_video.h>
|
||||||
|
|
||||||
FramerateManager::FramerateManager(int targetFrameRate)
|
FramerateManager::FramerateManager(int targetFrameRate)
|
||||||
: targetFrameTime(Duration(boost::chrono::seconds(1)) / targetFrameRate)
|
: targetFrameTime(Duration(boost::chrono::seconds(1)) / targetFrameRate)
|
||||||
, lastFrameIndex(0)
|
, lastFrameIndex(0)
|
||||||
, lastFrameTimes({})
|
, lastFrameTimes({})
|
||||||
, lastTimePoint (Clock::now())
|
, lastTimePoint(Clock::now())
|
||||||
|
, vsyncEnabled(settings["video"]["vsync"].Bool())
|
||||||
{
|
{
|
||||||
boost::range::fill(lastFrameTimes, targetFrameTime);
|
boost::range::fill(lastFrameTimes, targetFrameTime);
|
||||||
}
|
}
|
||||||
@ -24,9 +28,14 @@ void FramerateManager::framerateDelay()
|
|||||||
{
|
{
|
||||||
Duration timeSpentBusy = Clock::now() - lastTimePoint;
|
Duration timeSpentBusy = Clock::now() - lastTimePoint;
|
||||||
|
|
||||||
// FPS is higher than it should be, then wait some time
|
if(!vsyncEnabled)
|
||||||
if(timeSpentBusy < targetFrameTime)
|
{
|
||||||
boost::this_thread::sleep_for(targetFrameTime - timeSpentBusy);
|
// if FPS is higher than it should be, then wait some time
|
||||||
|
if(timeSpentBusy < targetFrameTime)
|
||||||
|
{
|
||||||
|
boost::this_thread::sleep_for(targetFrameTime - timeSpentBusy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// compute actual timeElapsed taking into account actual sleep interval
|
// compute actual timeElapsed taking into account actual sleep interval
|
||||||
// limit it to 100 ms to avoid breaking animation in case of huge lag (e.g. triggered breakpoint)
|
// limit it to 100 ms to avoid breaking animation in case of huge lag (e.g. triggered breakpoint)
|
||||||
|
@ -25,6 +25,8 @@ class FramerateManager
|
|||||||
/// index of last measured frome in lastFrameTimes array
|
/// index of last measured frome in lastFrameTimes array
|
||||||
ui32 lastFrameIndex;
|
ui32 lastFrameIndex;
|
||||||
|
|
||||||
|
bool vsyncEnabled;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FramerateManager(int targetFramerate);
|
FramerateManager(int targetFramerate);
|
||||||
|
|
||||||
|
0
client/icons/generate_icns.py
Normal file → Executable file
0
client/icons/generate_icns.py
Normal file → Executable file
@ -79,9 +79,9 @@ CBonusSelection::CBonusSelection()
|
|||||||
iconsMapSizes = std::make_shared<CAnimImage>(AnimationPath::builtin("SCNRMPSZ"), 4, 0, 735, 26);
|
iconsMapSizes = std::make_shared<CAnimImage>(AnimationPath::builtin("SCNRMPSZ"), 4, 0, 735, 26);
|
||||||
|
|
||||||
labelCampaignDescription = std::make_shared<CLabel>(481, 63, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::YELLOW, CGI->generaltexth->allTexts[38]);
|
labelCampaignDescription = std::make_shared<CLabel>(481, 63, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::YELLOW, CGI->generaltexth->allTexts[38]);
|
||||||
campaignDescription = std::make_shared<CTextBox>(getCampaign()->getDescription(), Rect(480, 86, 286, 117), 1);
|
campaignDescription = std::make_shared<CTextBox>(getCampaign()->getDescriptionTranslated(), Rect(480, 86, 286, 117), 1);
|
||||||
|
|
||||||
mapName = std::make_shared<CLabel>(481, 219, FONT_BIG, ETextAlignment::TOPLEFT, Colors::YELLOW, CSH->mi->getName());
|
mapName = std::make_shared<CLabel>(481, 219, FONT_BIG, ETextAlignment::TOPLEFT, Colors::YELLOW, CSH->mi->getNameTranslated());
|
||||||
labelMapDescription = std::make_shared<CLabel>(481, 253, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::YELLOW, CGI->generaltexth->allTexts[496]);
|
labelMapDescription = std::make_shared<CLabel>(481, 253, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::YELLOW, CGI->generaltexth->allTexts[496]);
|
||||||
mapDescription = std::make_shared<CTextBox>("", Rect(480, 278, 292, 108), 1);
|
mapDescription = std::make_shared<CTextBox>("", Rect(480, 278, 292, 108), 1);
|
||||||
|
|
||||||
@ -146,18 +146,18 @@ void CBonusSelection::createBonusesIcons()
|
|||||||
std::string picName = bonusPics[bonusType];
|
std::string picName = bonusPics[bonusType];
|
||||||
size_t picNumber = bonDescs[i].info2;
|
size_t picNumber = bonDescs[i].info2;
|
||||||
|
|
||||||
std::string desc;
|
MetaString desc;
|
||||||
switch(bonDescs[i].type)
|
switch(bonDescs[i].type)
|
||||||
{
|
{
|
||||||
case CampaignBonusType::SPELL:
|
case CampaignBonusType::SPELL:
|
||||||
desc = CGI->generaltexth->allTexts[715];
|
desc.appendLocalString(EMetaText::GENERAL_TXT, 715);
|
||||||
boost::algorithm::replace_first(desc, "%s", CGI->spells()->getByIndex(bonDescs[i].info2)->getNameTranslated());
|
desc.replaceLocalString(EMetaText::SPELL_NAME, bonDescs[i].info2);
|
||||||
break;
|
break;
|
||||||
case CampaignBonusType::MONSTER:
|
case CampaignBonusType::MONSTER:
|
||||||
picNumber = bonDescs[i].info2 + 2;
|
picNumber = bonDescs[i].info2 + 2;
|
||||||
desc = CGI->generaltexth->allTexts[717];
|
desc.appendLocalString(EMetaText::GENERAL_TXT, 717);
|
||||||
boost::algorithm::replace_first(desc, "%d", std::to_string(bonDescs[i].info3));
|
desc.replaceNumber(bonDescs[i].info3);
|
||||||
boost::algorithm::replace_first(desc, "%s", CGI->creatures()->getByIndex(bonDescs[i].info2)->getNamePluralTranslated());
|
desc.replaceLocalString(EMetaText::CRE_PL_NAMES, bonDescs[i].info2);
|
||||||
break;
|
break;
|
||||||
case CampaignBonusType::BUILDING:
|
case CampaignBonusType::BUILDING:
|
||||||
{
|
{
|
||||||
@ -182,17 +182,16 @@ void CBonusSelection::createBonusesIcons()
|
|||||||
picNumber = -1;
|
picNumber = -1;
|
||||||
|
|
||||||
if(vstd::contains((*CGI->townh)[faction]->town->buildings, buildID))
|
if(vstd::contains((*CGI->townh)[faction]->town->buildings, buildID))
|
||||||
desc = (*CGI->townh)[faction]->town->buildings.find(buildID)->second->getNameTranslated();
|
desc.appendTextID((*CGI->townh)[faction]->town->buildings.find(buildID)->second->getNameTextID());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CampaignBonusType::ARTIFACT:
|
case CampaignBonusType::ARTIFACT:
|
||||||
desc = CGI->generaltexth->allTexts[715];
|
desc.appendLocalString(EMetaText::GENERAL_TXT, 715);
|
||||||
boost::algorithm::replace_first(desc, "%s", CGI->artifacts()->getByIndex(bonDescs[i].info2)->getNameTranslated());
|
desc.replaceLocalString(EMetaText::ART_NAMES, bonDescs[i].info2);
|
||||||
break;
|
break;
|
||||||
case CampaignBonusType::SPELL_SCROLL:
|
case CampaignBonusType::SPELL_SCROLL:
|
||||||
desc = CGI->generaltexth->allTexts[716];
|
desc.appendLocalString(EMetaText::GENERAL_TXT, 716);
|
||||||
boost::algorithm::replace_first(desc, "%s", CGI->spells()->getByIndex(bonDescs[i].info2)->getNameTranslated());
|
desc.replaceLocalString(EMetaText::ART_NAMES, bonDescs[i].info2);
|
||||||
break;
|
break;
|
||||||
case CampaignBonusType::PRIMARY_SKILL:
|
case CampaignBonusType::PRIMARY_SKILL:
|
||||||
{
|
{
|
||||||
@ -211,7 +210,7 @@ void CBonusSelection::createBonusesIcons()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
picNumber = leadingSkill;
|
picNumber = leadingSkill;
|
||||||
desc = CGI->generaltexth->allTexts[715];
|
desc.appendLocalString(EMetaText::GENERAL_TXT, 715);
|
||||||
|
|
||||||
std::string substitute; //text to be printed instead of %s
|
std::string substitute; //text to be printed instead of %s
|
||||||
for(int v = 0; v < toPrint.size(); ++v)
|
for(int v = 0; v < toPrint.size(); ++v)
|
||||||
@ -224,14 +223,13 @@ void CBonusSelection::createBonusesIcons()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::algorithm::replace_first(desc, "%s", substitute);
|
desc.replaceRawString(substitute);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CampaignBonusType::SECONDARY_SKILL:
|
case CampaignBonusType::SECONDARY_SKILL:
|
||||||
desc = CGI->generaltexth->allTexts[718];
|
desc.appendLocalString(EMetaText::GENERAL_TXT, 718);
|
||||||
|
desc.replaceTextID(TextIdentifier("core", "genrltxt", "levels", bonDescs[i].info3 - 1).get());
|
||||||
boost::algorithm::replace_first(desc, "%s", CGI->generaltexth->levels[bonDescs[i].info3 - 1]); //skill level
|
desc.replaceLocalString(EMetaText::SEC_SKILL_NAME, bonDescs[i].info2);
|
||||||
boost::algorithm::replace_first(desc, "%s", CGI->skillh->getByIndex(bonDescs[i].info2)->getNameTranslated()); //skill name
|
|
||||||
picNumber = bonDescs[i].info2 * 3 + bonDescs[i].info3 - 1;
|
picNumber = bonDescs[i].info2 * 3 + bonDescs[i].info3 - 1;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -258,18 +256,17 @@ void CBonusSelection::createBonusesIcons()
|
|||||||
}
|
}
|
||||||
picNumber = serialResID;
|
picNumber = serialResID;
|
||||||
|
|
||||||
desc = CGI->generaltexth->allTexts[717];
|
desc.appendLocalString(EMetaText::GENERAL_TXT, 717);
|
||||||
boost::algorithm::replace_first(desc, "%d", std::to_string(bonDescs[i].info2));
|
desc.replaceNumber(bonDescs[i].info2);
|
||||||
std::string replacement;
|
|
||||||
if(serialResID <= 6)
|
if(serialResID <= 6)
|
||||||
{
|
{
|
||||||
replacement = CGI->generaltexth->restypes[serialResID];
|
desc.replaceLocalString(EMetaText::RES_NAMES, serialResID);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
replacement = CGI->generaltexth->allTexts[714 + serialResID];
|
desc.replaceLocalString(EMetaText::GENERAL_TXT, 714 + serialResID);
|
||||||
}
|
}
|
||||||
boost::algorithm::replace_first(desc, "%s", replacement);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CampaignBonusType::HEROES_FROM_PREVIOUS_SCENARIO:
|
case CampaignBonusType::HEROES_FROM_PREVIOUS_SCENARIO:
|
||||||
@ -278,31 +275,29 @@ void CBonusSelection::createBonusesIcons()
|
|||||||
if(!superhero)
|
if(!superhero)
|
||||||
logGlobal->warn("No superhero! How could it be transferred?");
|
logGlobal->warn("No superhero! How could it be transferred?");
|
||||||
picNumber = superhero ? superhero->portrait : 0;
|
picNumber = superhero ? superhero->portrait : 0;
|
||||||
desc = CGI->generaltexth->allTexts[719];
|
desc.appendLocalString(EMetaText::GENERAL_TXT, 719);
|
||||||
|
desc.replaceRawString(getCampaign()->scenario(static_cast<CampaignScenarioID>(bonDescs[i].info2)).scenarioName.toString());
|
||||||
boost::algorithm::replace_first(desc, "%s", getCampaign()->scenario(static_cast<CampaignScenarioID>(bonDescs[i].info2)).scenarioName);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case CampaignBonusType::HERO:
|
case CampaignBonusType::HERO:
|
||||||
|
|
||||||
desc = CGI->generaltexth->allTexts[718];
|
desc.appendLocalString(EMetaText::GENERAL_TXT, 718);
|
||||||
boost::algorithm::replace_first(desc, "%s", CGI->generaltexth->capColors[bonDescs[i].info1]); //hero's color
|
desc.replaceTextID(TextIdentifier("core", "genrltxt", "capColors", bonDescs[i].info1).get());
|
||||||
|
|
||||||
if(bonDescs[i].info2 == 0xFFFF)
|
if(bonDescs[i].info2 == 0xFFFF)
|
||||||
{
|
{
|
||||||
boost::algorithm::replace_first(desc, "%s", CGI->generaltexth->allTexts[101]); //hero's name
|
desc.replaceLocalString(EMetaText::GENERAL_TXT, 101);
|
||||||
picNumber = -1;
|
picNumber = -1;
|
||||||
picName = "CBONN1A3.BMP";
|
picName = "CBONN1A3.BMP";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
boost::algorithm::replace_first(desc, "%s", CGI->heroh->objects[bonDescs[i].info2]->getNameTranslated());
|
desc.replaceTextID(CGI->heroh->objects[bonDescs[i].info2]->getNameTextID());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<CToggleButton> bonusButton = std::make_shared<CToggleButton>(Point(475 + i * 68, 455), AnimationPath(), CButton::tooltip(desc, desc));
|
std::shared_ptr<CToggleButton> bonusButton = std::make_shared<CToggleButton>(Point(475 + i * 68, 455), AnimationPath(), CButton::tooltip(desc.toString(), desc.toString()));
|
||||||
|
|
||||||
if(picNumber != -1)
|
if(picNumber != -1)
|
||||||
picName += ":" + std::to_string(picNumber);
|
picName += ":" + std::to_string(picNumber);
|
||||||
@ -355,8 +350,8 @@ void CBonusSelection::updateAfterStateChange()
|
|||||||
if(!CSH->mi)
|
if(!CSH->mi)
|
||||||
return;
|
return;
|
||||||
iconsMapSizes->setFrame(CSH->mi->getMapSizeIconId());
|
iconsMapSizes->setFrame(CSH->mi->getMapSizeIconId());
|
||||||
mapName->setText(CSH->mi->getName());
|
mapName->setText(CSH->mi->getNameTranslated());
|
||||||
mapDescription->setText(CSH->mi->getDescription());
|
mapDescription->setText(CSH->mi->getDescriptionTranslated());
|
||||||
for(size_t i = 0; i < difficultyIcons.size(); i++)
|
for(size_t i = 0; i < difficultyIcons.size(); i++)
|
||||||
{
|
{
|
||||||
if(i == CSH->si->difficulty)
|
if(i == CSH->si->difficulty)
|
||||||
@ -514,9 +509,9 @@ void CBonusSelection::CRegion::clickReleased(const Point & cursorPosition)
|
|||||||
void CBonusSelection::CRegion::showPopupWindow(const Point & cursorPosition)
|
void CBonusSelection::CRegion::showPopupWindow(const Point & cursorPosition)
|
||||||
{
|
{
|
||||||
// FIXME: For some reason "down" is only ever contain indeterminate_value
|
// FIXME: For some reason "down" is only ever contain indeterminate_value
|
||||||
auto text = CSH->si->campState->scenario(idOfMapAndRegion).regionText;
|
auto & text = CSH->si->campState->scenario(idOfMapAndRegion).regionText;
|
||||||
if(!graphicsNotSelected->getSurface()->isTransparent(cursorPosition - pos.topLeft()) && text.size())
|
if(!graphicsNotSelected->getSurface()->isTransparent(cursorPosition - pos.topLeft()) && !text.empty())
|
||||||
{
|
{
|
||||||
CRClickPopup::createAndPush(text);
|
CRClickPopup::createAndPush(text.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,8 +200,8 @@ void InfoCard::changeSelection()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
labelSaveDate->setText(mapInfo->date);
|
labelSaveDate->setText(mapInfo->date);
|
||||||
mapName->setText(mapInfo->getName());
|
mapName->setText(mapInfo->getNameTranslated());
|
||||||
mapDescription->setText(mapInfo->getDescription());
|
mapDescription->setText(mapInfo->getDescriptionTranslated());
|
||||||
|
|
||||||
mapDescription->label->scrollTextTo(0, false);
|
mapDescription->label->scrollTextTo(0, false);
|
||||||
if(mapDescription->slider)
|
if(mapDescription->slider)
|
||||||
|
@ -405,8 +405,8 @@ std::string OptionsTab::CPlayerSettingsHelper::getName()
|
|||||||
return CGI->generaltexth->allTexts[522];
|
return CGI->generaltexth->allTexts[522];
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
if(!playerSettings.heroName.empty())
|
if(!playerSettings.heroNameTextId.empty())
|
||||||
return playerSettings.heroName;
|
return playerSettings.heroNameTextId;
|
||||||
auto index = playerSettings.hero.getNum() >= CGI->heroh->size() ? 0 : playerSettings.hero.getNum();
|
auto index = playerSettings.hero.getNum() >= CGI->heroh->size() ? 0 : playerSettings.hero.getNum();
|
||||||
return (*CGI->heroh)[index]->getNameTranslated();
|
return (*CGI->heroh)[index]->getNameTranslated();
|
||||||
}
|
}
|
||||||
@ -927,7 +927,7 @@ void OptionsTab::SelectionWindow::setElement(int elem, bool doApply)
|
|||||||
if(!doApply)
|
if(!doApply)
|
||||||
{
|
{
|
||||||
CPlayerSettingsHelper helper = CPlayerSettingsHelper(set, SelType::HERO);
|
CPlayerSettingsHelper helper = CPlayerSettingsHelper(set, SelType::HERO);
|
||||||
if(settings["general"]["enableUiEnhancements"].Bool() && helper.playerSettings.hero.getNum() > PlayerSettings::RANDOM && helper.playerSettings.heroName.empty())
|
if(settings["general"]["enableUiEnhancements"].Bool() && helper.playerSettings.hero.getNum() > PlayerSettings::RANDOM && helper.playerSettings.heroNameTextId.empty())
|
||||||
GH.windows().createAndPushWindow<CHeroOverview>(helper.playerSettings.hero);
|
GH.windows().createAndPushWindow<CHeroOverview>(helper.playerSettings.hero);
|
||||||
else
|
else
|
||||||
GH.windows().createAndPushWindow<CPlayerOptionTooltipBox>(helper);
|
GH.windows().createAndPushWindow<CPlayerOptionTooltipBox>(helper);
|
||||||
@ -1013,7 +1013,7 @@ void OptionsTab::SelectedBox::showPopupWindow(const Point & cursorPosition)
|
|||||||
if(playerSettings.hero.getNum() == PlayerSettings::NONE && !SEL->getPlayerInfo(playerSettings.color.getNum()).hasCustomMainHero() && CPlayerSettingsHelper::type == HERO)
|
if(playerSettings.hero.getNum() == PlayerSettings::NONE && !SEL->getPlayerInfo(playerSettings.color.getNum()).hasCustomMainHero() && CPlayerSettingsHelper::type == HERO)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(settings["general"]["enableUiEnhancements"].Bool() && CPlayerSettingsHelper::type == HERO && playerSettings.hero.getNum() > PlayerSettings::RANDOM && playerSettings.heroName.empty())
|
if(settings["general"]["enableUiEnhancements"].Bool() && CPlayerSettingsHelper::type == HERO && playerSettings.hero.getNum() > PlayerSettings::RANDOM && playerSettings.heroNameTextId.empty())
|
||||||
GH.windows().createAndPushWindow<CHeroOverview>(playerSettings.hero);
|
GH.windows().createAndPushWindow<CHeroOverview>(playerSettings.hero);
|
||||||
else
|
else
|
||||||
GH.windows().createAndPushWindow<CPlayerOptionTooltipBox>(*this);
|
GH.windows().createAndPushWindow<CPlayerOptionTooltipBox>(*this);
|
||||||
|
@ -163,8 +163,8 @@ void RandomMapTab::updateMapInfoByHost()
|
|||||||
mapInfo->isRandomMap = true;
|
mapInfo->isRandomMap = true;
|
||||||
mapInfo->mapHeader = std::make_unique<CMapHeader>();
|
mapInfo->mapHeader = std::make_unique<CMapHeader>();
|
||||||
mapInfo->mapHeader->version = EMapFormat::VCMI;
|
mapInfo->mapHeader->version = EMapFormat::VCMI;
|
||||||
mapInfo->mapHeader->name = CGI->generaltexth->allTexts[740];
|
mapInfo->mapHeader->name.appendLocalString(EMetaText::GENERAL_TXT, 740);
|
||||||
mapInfo->mapHeader->description = CGI->generaltexth->allTexts[741];
|
mapInfo->mapHeader->description.appendLocalString(EMetaText::GENERAL_TXT, 741);
|
||||||
mapInfo->mapHeader->difficulty = 1; // Normal
|
mapInfo->mapHeader->difficulty = 1; // Normal
|
||||||
mapInfo->mapHeader->height = mapGenOptions->getHeight();
|
mapInfo->mapHeader->height = mapGenOptions->getHeight();
|
||||||
mapInfo->mapHeader->width = mapGenOptions->getWidth();
|
mapInfo->mapHeader->width = mapGenOptions->getWidth();
|
||||||
|
@ -108,11 +108,11 @@ bool mapSorter::operator()(const std::shared_ptr<ElementInfo> aaa, const std::sh
|
|||||||
return (a->victoryIconIndex < b->victoryIconIndex);
|
return (a->victoryIconIndex < b->victoryIconIndex);
|
||||||
break;
|
break;
|
||||||
case _name: //by name
|
case _name: //by name
|
||||||
return boost::ilexicographical_compare(a->name, b->name);
|
return boost::ilexicographical_compare(a->name.toString(), b->name.toString());
|
||||||
case _fileName: //by filename
|
case _fileName: //by filename
|
||||||
return boost::ilexicographical_compare(aaa->fileURI, bbb->fileURI);
|
return boost::ilexicographical_compare(aaa->fileURI, bbb->fileURI);
|
||||||
default:
|
default:
|
||||||
return boost::ilexicographical_compare(a->name, b->name);
|
return boost::ilexicographical_compare(a->name.toString(), b->name.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else //if we are sorting campaigns
|
else //if we are sorting campaigns
|
||||||
@ -122,9 +122,9 @@ bool mapSorter::operator()(const std::shared_ptr<ElementInfo> aaa, const std::sh
|
|||||||
case _numOfMaps: //by number of maps in campaign
|
case _numOfMaps: //by number of maps in campaign
|
||||||
return aaa->campaign->scenariosCount() < bbb->campaign->scenariosCount();
|
return aaa->campaign->scenariosCount() < bbb->campaign->scenariosCount();
|
||||||
case _name: //by name
|
case _name: //by name
|
||||||
return boost::ilexicographical_compare(aaa->campaign->getName(), bbb->campaign->getName());
|
return boost::ilexicographical_compare(aaa->campaign->getNameTranslated(), bbb->campaign->getNameTranslated());
|
||||||
default:
|
default:
|
||||||
return boost::ilexicographical_compare(aaa->campaign->getName(), bbb->campaign->getName());
|
return boost::ilexicographical_compare(aaa->campaign->getNameTranslated(), bbb->campaign->getNameTranslated());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -363,7 +363,7 @@ void SelectionTab::showPopupWindow(const Point & cursorPosition)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if(!curItems[py]->isFolder)
|
if(!curItems[py]->isFolder)
|
||||||
GH.windows().createAndPushWindow<CMapOverview>(curItems[py]->getName(), curItems[py]->fullFileURI, curItems[py]->date, ResourcePath(curItems[py]->fileURI), tabType);
|
GH.windows().createAndPushWindow<CMapOverview>(curItems[py]->getNameTranslated(), curItems[py]->fullFileURI, curItems[py]->date, ResourcePath(curItems[py]->fileURI), tabType);
|
||||||
else
|
else
|
||||||
CRClickPopup::createAndPush(curItems[py]->folderName);
|
CRClickPopup::createAndPush(curItems[py]->folderName);
|
||||||
}
|
}
|
||||||
|
@ -105,7 +105,7 @@ CCampaignScreen::CCampaignButton::CCampaignButton(const JsonNode & config, const
|
|||||||
status = CCampaignScreen::ENABLED;
|
status = CCampaignScreen::ENABLED;
|
||||||
|
|
||||||
auto header = CampaignHandler::getHeader(campFile);
|
auto header = CampaignHandler::getHeader(campFile);
|
||||||
hoverText = header->getName();
|
hoverText = header->getNameTranslated();
|
||||||
|
|
||||||
if(persistentStorage["completedCampaigns"][header->getFilename()].Bool())
|
if(persistentStorage["completedCampaigns"][header->getFilename()].Bool())
|
||||||
status = CCampaignScreen::COMPLETED;
|
status = CCampaignScreen::COMPLETED;
|
||||||
|
@ -36,7 +36,7 @@ CPrologEpilogVideo::CPrologEpilogVideo(CampaignScenarioPrologEpilog _spe, std::f
|
|||||||
};
|
};
|
||||||
CCS->soundh->setCallback(voiceSoundHandle, onVoiceStop);
|
CCS->soundh->setCallback(voiceSoundHandle, onVoiceStop);
|
||||||
|
|
||||||
text = std::make_shared<CMultiLineLabel>(Rect(100, 500, 600, 100), EFonts::FONT_BIG, ETextAlignment::CENTER, Colors::METALLIC_GOLD, spe.prologText);
|
text = std::make_shared<CMultiLineLabel>(Rect(100, 500, 600, 100), EFonts::FONT_BIG, ETextAlignment::CENTER, Colors::METALLIC_GOLD, spe.prologText.toString());
|
||||||
text->scrollTextTo(-100);
|
text->scrollTextTo(-100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,8 +274,14 @@ void ScreenHandler::initializeWindow()
|
|||||||
handleFatalError(message, true);
|
handleFatalError(message, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
//create first available renderer if preferred not set. Use no flags, so HW accelerated will be preferred but SW renderer also will possible
|
// create first available renderer if no preferred one is set
|
||||||
mainRenderer = SDL_CreateRenderer(mainWindow, getPreferredRenderingDriver(), 0);
|
// use no SDL_RENDERER_SOFTWARE or SDL_RENDERER_ACCELERATED flag, so HW accelerated will be preferred but SW renderer will also be possible
|
||||||
|
uint32_t rendererFlags = 0;
|
||||||
|
if(settings["video"]["vsync"].Bool())
|
||||||
|
{
|
||||||
|
rendererFlags |= SDL_RENDERER_PRESENTVSYNC;
|
||||||
|
}
|
||||||
|
mainRenderer = SDL_CreateRenderer(mainWindow, getPreferredRenderingDriver(), rendererFlags);
|
||||||
|
|
||||||
if(mainRenderer == nullptr)
|
if(mainRenderer == nullptr)
|
||||||
throw std::runtime_error("Unable to create renderer\n");
|
throw std::runtime_error("Unable to create renderer\n");
|
||||||
@ -570,4 +576,4 @@ bool ScreenHandler::hasFocus()
|
|||||||
{
|
{
|
||||||
ui32 flags = SDL_GetWindowFlags(mainWindow);
|
ui32 flags = SDL_GetWindowFlags(mainWindow);
|
||||||
return flags & SDL_WINDOW_INPUT_FOCUS;
|
return flags & SDL_WINDOW_INPUT_FOCUS;
|
||||||
}
|
}
|
||||||
|
@ -282,7 +282,7 @@ bool ArtifactUtilsClient::askToAssemble(const CGHeroInstance * hero, const Artif
|
|||||||
if(hero->tempOwner != LOCPLINT->playerID)
|
if(hero->tempOwner != LOCPLINT->playerID)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto assemblyPossibilities = ArtifactUtils::assemblyPossibilities(hero, art->getTypeId(), ArtifactUtils::isSlotEquipment(slot));
|
auto assemblyPossibilities = ArtifactUtils::assemblyPossibilities(hero, art->getTypeId());
|
||||||
if(!assemblyPossibilities.empty())
|
if(!assemblyPossibilities.empty())
|
||||||
{
|
{
|
||||||
auto askThread = new boost::thread([hero, art, slot, assemblyPossibilities]() -> void
|
auto askThread = new boost::thread([hero, art, slot, assemblyPossibilities]() -> void
|
||||||
|
@ -261,8 +261,10 @@ void CArtifactsOfHeroBase::setSlotData(ArtPlacePtr artPlace, const ArtifactPosit
|
|||||||
{
|
{
|
||||||
arts.insert(std::pair(combinedArt, 0));
|
arts.insert(std::pair(combinedArt, 0));
|
||||||
for(const auto part : combinedArt->getConstituents())
|
for(const auto part : combinedArt->getConstituents())
|
||||||
if(artSet.hasArt(part->getId(), true))
|
{
|
||||||
|
if(artSet.hasArt(part->getId(), false))
|
||||||
arts.at(combinedArt)++;
|
arts.at(combinedArt)++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
artPlace->addCombinedArtInfo(arts);
|
artPlace->addCombinedArtInfo(arts);
|
||||||
}
|
}
|
||||||
|
@ -254,7 +254,7 @@ void CWindowWithArtifacts::rightClickArtPlaceHero(CArtifactsOfHeroBase & artsIns
|
|||||||
|
|
||||||
void CWindowWithArtifacts::artifactRemoved(const ArtifactLocation & artLoc)
|
void CWindowWithArtifacts::artifactRemoved(const ArtifactLocation & artLoc)
|
||||||
{
|
{
|
||||||
updateSlots(artLoc.slot);
|
updateSlots();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CWindowWithArtifacts::artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc, bool withRedraw)
|
void CWindowWithArtifacts::artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc, bool withRedraw)
|
||||||
@ -329,26 +329,23 @@ void CWindowWithArtifacts::artifactMoved(const ArtifactLocation & srcLoc, const
|
|||||||
|
|
||||||
void CWindowWithArtifacts::artifactDisassembled(const ArtifactLocation & artLoc)
|
void CWindowWithArtifacts::artifactDisassembled(const ArtifactLocation & artLoc)
|
||||||
{
|
{
|
||||||
updateSlots(artLoc.slot);
|
updateSlots();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CWindowWithArtifacts::artifactAssembled(const ArtifactLocation & artLoc)
|
void CWindowWithArtifacts::artifactAssembled(const ArtifactLocation & artLoc)
|
||||||
{
|
{
|
||||||
markPossibleSlots();
|
markPossibleSlots();
|
||||||
updateSlots(artLoc.slot);
|
updateSlots();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CWindowWithArtifacts::updateSlots(const ArtifactPosition & slot)
|
void CWindowWithArtifacts::updateSlots()
|
||||||
{
|
{
|
||||||
auto updateSlotBody = [slot](auto artSetWeak) -> void
|
auto updateSlotBody = [](auto artSetWeak) -> void
|
||||||
{
|
{
|
||||||
if(const auto artSetPtr = artSetWeak.lock())
|
if(const auto artSetPtr = artSetWeak.lock())
|
||||||
{
|
{
|
||||||
if(ArtifactUtils::isSlotEquipment(slot))
|
artSetPtr->updateWornSlots();
|
||||||
artSetPtr->updateWornSlots();
|
artSetPtr->updateBackpackSlots();
|
||||||
else if(ArtifactUtils::isSlotBackpack(slot))
|
|
||||||
artSetPtr->updateBackpackSlots();
|
|
||||||
|
|
||||||
artSetPtr->redraw();
|
artSetPtr->redraw();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -44,7 +44,7 @@ protected:
|
|||||||
std::vector<CArtifactsOfHeroPtr> artSets;
|
std::vector<CArtifactsOfHeroPtr> artSets;
|
||||||
CloseCallback closeCallback;
|
CloseCallback closeCallback;
|
||||||
|
|
||||||
void updateSlots(const ArtifactPosition & slot);
|
void updateSlots();
|
||||||
std::optional<std::tuple<const CGHeroInstance*, const CArtifactInstance*>> getState();
|
std::optional<std::tuple<const CGHeroInstance*, const CArtifactInstance*>> getState();
|
||||||
std::optional<CArtifactsOfHeroPtr> findAOHbyRef(CArtifactsOfHeroBase & artsInst);
|
std::optional<CArtifactsOfHeroPtr> findAOHbyRef(CArtifactsOfHeroBase & artsInst);
|
||||||
void markPossibleSlots();
|
void markPossibleSlots();
|
||||||
|
@ -456,8 +456,7 @@ CTavernWindow::CTavernWindow(const CGObjectInstance * TavernObj)
|
|||||||
heroDescription = std::make_shared<CTextBox>("", Rect(30, 373, 233, 35), 0, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE);
|
heroDescription = std::make_shared<CTextBox>("", Rect(30, 373, 233, 35), 0, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE);
|
||||||
heroesForHire = std::make_shared<CLabel>(145, 283, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->jktexts[38]);
|
heroesForHire = std::make_shared<CLabel>(145, 283, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->jktexts[38]);
|
||||||
|
|
||||||
auto rumorText = boost::str(boost::format(CGI->generaltexth->allTexts[216]) % LOCPLINT->cb->getTavernRumor(tavernObj));
|
rumor = std::make_shared<CTextBox>(LOCPLINT->cb->getTavernRumor(tavernObj), Rect(32, 188, 330, 66), 0, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE);
|
||||||
rumor = std::make_shared<CTextBox>(rumorText, Rect(32, 188, 330, 66), 0, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE);
|
|
||||||
|
|
||||||
statusbar = CGStatusBar::create(std::make_shared<CPicture>(background->getSurface(), Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26));
|
statusbar = CGStatusBar::create(std::make_shared<CPicture>(background->getSurface(), Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26));
|
||||||
cancel = std::make_shared<CButton>(Point(310, 428), AnimationPath::builtin("ICANCEL.DEF"), CButton::tooltip(CGI->generaltexth->tavernInfo[7]), std::bind(&CTavernWindow::close, this), EShortcut::GLOBAL_CANCEL);
|
cancel = std::make_shared<CButton>(Point(310, 428), AnimationPath::builtin("ICANCEL.DEF"), CButton::tooltip(CGI->generaltexth->tavernInfo[7]), std::bind(&CTavernWindow::close, this), EShortcut::GLOBAL_CANCEL);
|
||||||
|
@ -149,7 +149,8 @@
|
|||||||
"driver",
|
"driver",
|
||||||
"displayIndex",
|
"displayIndex",
|
||||||
"showfps",
|
"showfps",
|
||||||
"targetfps"
|
"targetfps",
|
||||||
|
"vsync"
|
||||||
],
|
],
|
||||||
"properties" : {
|
"properties" : {
|
||||||
"resolution" : {
|
"resolution" : {
|
||||||
@ -207,6 +208,10 @@
|
|||||||
"targetfps" : {
|
"targetfps" : {
|
||||||
"type" : "number",
|
"type" : "number",
|
||||||
"default" : 60
|
"default" : 60
|
||||||
|
},
|
||||||
|
"vsync" : {
|
||||||
|
"type" : "boolean",
|
||||||
|
"default" : true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -10,17 +10,17 @@ The following instructions apply to **v1.2 and later**. For earlier versions the
|
|||||||
2. JDK 11, not necessarily from Oracle
|
2. JDK 11, not necessarily from Oracle
|
||||||
3. Android command line tools or Android Studio for your OS: https://developer.android.com/studio/
|
3. Android command line tools or Android Studio for your OS: https://developer.android.com/studio/
|
||||||
4. Android NDK version **r25c (25.2.9519653)**, there're multiple ways to obtain it:
|
4. Android NDK version **r25c (25.2.9519653)**, there're multiple ways to obtain it:
|
||||||
- - install with Android Studio
|
- install with Android Studio
|
||||||
- - install with `sdkmanager` command line tool
|
- install with `sdkmanager` command line tool
|
||||||
- - download from https://developer.android.com/ndk/downloads
|
- download from https://developer.android.com/ndk/downloads
|
||||||
- - download with Conan, see [#NDK and Conan](#ndk-and-conan)
|
- download with Conan, see [#NDK and Conan](#ndk-and-conan)
|
||||||
5. (optional) Ninja: download from your package manager or from https://github.com/ninja-build/ninja/releases
|
5. (optional) Ninja: download from your package manager or from https://github.com/ninja-build/ninja/releases
|
||||||
|
|
||||||
## Obtaining source code
|
## Obtaining source code
|
||||||
|
|
||||||
Clone https://github.com/vcmi/vcmi with submodules. Example for command line:
|
Clone https://github.com/vcmi/vcmi with submodules. Example for command line:
|
||||||
|
|
||||||
```sh
|
```
|
||||||
git clone --recurse-submodules https://github.com/vcmi/vcmi.git
|
git clone --recurse-submodules https://github.com/vcmi/vcmi.git
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -30,24 +30,24 @@ We use Conan package manager to build/consume dependencies, find detailed usage
|
|||||||
branch](https://github.com/vcmi/vcmi/tree/master/docs/conan.md).
|
branch](https://github.com/vcmi/vcmi/tree/master/docs/conan.md).
|
||||||
|
|
||||||
On the step where you need to replace **PROFILE**, choose:
|
On the step where you need to replace **PROFILE**, choose:
|
||||||
- `android-32` to build for 32-bit architecture (armeabi-v7a)
|
- `android-32` to build for 32-bit architecture (armeabi-v7a)
|
||||||
- `android-64` to build for 64-bit architecture (aarch64-v8a)
|
- `android-64` to build for 64-bit architecture (aarch64-v8a)
|
||||||
|
|
||||||
### NDK and Conan
|
### NDK and Conan
|
||||||
|
|
||||||
Conan must be aware of the NDK location when you execute `conan install`. There're multiple ways to achieve that as written in the [Conan docs](https://docs.conan.io/1/integrations/cross_platform/android.html):
|
Conan must be aware of the NDK location when you execute `conan install`. There're multiple ways to achieve that as written in the [Conan docs](https://docs.conan.io/1/integrations/cross_platform/android.html):
|
||||||
|
|
||||||
- the easiest is to download NDK from Conan (option 1 in the docs), then all the magic happens automatically. You need to create your own Conan profile that imports our Android profile and adds 2 new lines (you can of course just copy everything from our profile into yours without including) and then pass this new profile to `conan install`:
|
- the easiest is to download NDK from Conan (option 1 in the docs), then all the magic happens automatically. You need to create your own Conan profile that imports our Android profile and adds 2 new lines (you can of course just copy everything from our profile into yours without including) and then pass this new profile to `conan install`:
|
||||||
|
|
||||||
```sh
|
```
|
||||||
include(/path/to/vcmi/CI/conan/android-64)
|
include(/path/to/vcmi/CI/conan/android-64)
|
||||||
[tool_requires]
|
[tool_requires]
|
||||||
android-ndk/r25c
|
android-ndk/r25c
|
||||||
```
|
```
|
||||||
|
|
||||||
- to use an already installed NDK, you can simply pass it on the command line to `conan install`:
|
- to use an already installed NDK, you can simply pass it on the command line to `conan install`:
|
||||||
|
|
||||||
```sh
|
```
|
||||||
conan install -c tools.android:ndk_path=/path/to/ndk ...
|
conan install -c tools.android:ndk_path=/path/to/ndk ...
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ Building for Android is a 2-step process. First, native C++ code is compiled to
|
|||||||
|
|
||||||
This is a traditional CMake project, you can build it from command line or some IDE. You're not required to pass any custom options (except Conan toolchain file), defaults are already good. If you wish to use your own CMake presets, inherit them from our `build-with-conan` preset. Example:
|
This is a traditional CMake project, you can build it from command line or some IDE. You're not required to pass any custom options (except Conan toolchain file), defaults are already good. If you wish to use your own CMake presets, inherit them from our `build-with-conan` preset. Example:
|
||||||
|
|
||||||
```sh
|
```
|
||||||
cmake -S . -B ../build -G Ninja -D CMAKE_BUILD_TYPE=Debug --toolchain ...
|
cmake -S . -B ../build -G Ninja -D CMAKE_BUILD_TYPE=Debug --toolchain ...
|
||||||
cmake --build ../build
|
cmake --build ../build
|
||||||
```
|
```
|
||||||
@ -83,4 +83,4 @@ cd android
|
|||||||
|
|
||||||
APK will appear in `android/vcmi-app/build/outputs/apk/debug` directory which you can then install to your device with `adb install -r /path/to/apk` (adb command is from Android command line tools).
|
APK will appear in `android/vcmi-app/build/outputs/apk/debug` directory which you can then install to your device with `adb install -r /path/to/apk` (adb command is from Android command line tools).
|
||||||
|
|
||||||
If you wish to build and install to your device in single action, use `installDebug` instead of `assembleDebug`.
|
If you wish to build and install to your device in single action, use `installDebug` instead of `assembleDebug`.
|
||||||
|
@ -2,22 +2,24 @@
|
|||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
1. **macOS**
|
1. **macOS**
|
||||||
2. Xcode: <https://developer.apple.com/xcode/>
|
2. Xcode: <https://developer.apple.com/xcode/>
|
||||||
3. CMake 3.21+: `brew install --cask cmake` or get from <https://cmake.org/download/>
|
3. CMake 3.21+: `brew install --cask cmake` or get from <https://cmake.org/download/>
|
||||||
|
|
||||||
## Obtaining source code
|
## Obtaining source code
|
||||||
|
|
||||||
Clone <https://github.com/vcmi/vcmi> with submodules. Example for command line:
|
Clone <https://github.com/vcmi/vcmi> with submodules. Example for command line:
|
||||||
|
|
||||||
`git clone --recurse-submodules https://github.com/vcmi/vcmi.git`
|
```
|
||||||
|
git clone --recurse-submodules https://github.com/vcmi/vcmi.git
|
||||||
|
```
|
||||||
|
|
||||||
## Obtaining dependencies
|
## Obtaining dependencies
|
||||||
|
|
||||||
There are 2 ways to get prebuilt dependencies:
|
There are 2 ways to get prebuilt dependencies:
|
||||||
|
|
||||||
- [Conan package manager](https://github.com/vcmi/vcmi/tree/develop/docs/conan.md) - recommended. Note that the link points to the cutting-edge state in `develop` branch, for the latest release check the same document in the [master branch (https://github.com/vcmi/vcmi/tree/master/docs/conan.md).
|
- [Conan package manager](https://github.com/vcmi/vcmi/tree/develop/docs/conan.md) - recommended. Note that the link points to the cutting-edge state in `develop` branch, for the latest release check the same document in the [master branch (https://github.com/vcmi/vcmi/tree/master/docs/conan.md).
|
||||||
- [legacy manually built libraries](https://github.com/vcmi/vcmi-ios-deps) - can be used if you have Xcode 11/12 or to build for simulator / armv7 device
|
- [legacy manually built libraries](https://github.com/vcmi/vcmi-ios-deps) - can be used if you have Xcode 11/12 or to build for simulator / armv7 device
|
||||||
|
|
||||||
## Configuring project
|
## Configuring project
|
||||||
|
|
||||||
@ -25,15 +27,17 @@ Only Xcode generator (`-G Xcode`) is supported!
|
|||||||
|
|
||||||
As a minimum, you must pass the following variables to CMake:
|
As a minimum, you must pass the following variables to CMake:
|
||||||
|
|
||||||
- `BUNDLE_IDENTIFIER_PREFIX`: unique bundle identifier prefix, something like `com.MY-NAME`
|
- `BUNDLE_IDENTIFIER_PREFIX`: unique bundle identifier prefix, something like `com.MY-NAME`
|
||||||
- (if using legacy dependencies) `CMAKE_PREFIX_PATH`: path to the downloaded dependencies, e.g. `~/Downloads/vcmi-ios-depends/build/iphoneos`
|
- (if using legacy dependencies) `CMAKE_PREFIX_PATH`: path to the downloaded dependencies, e.g. `~/Downloads/vcmi-ios-depends/build/iphoneos`
|
||||||
|
|
||||||
There're a few [CMake presets](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html): for device (Conan and legacy dependencies) and for simulator, named `ios-device-conan`, `ios-device` and `ios-simulator` respectively. You can also create your local "user preset" to avoid typing variables each time, see example [here](https://gist.github.com/kambala-decapitator/59438030c34b53aed7d3895aaa48b718).
|
There're a few [CMake presets](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html): for device (Conan and legacy dependencies) and for simulator, named `ios-device-conan`, `ios-device` and `ios-simulator` respectively. You can also create your local "user preset" to avoid typing variables each time, see example [here](https://gist.github.com/kambala-decapitator/59438030c34b53aed7d3895aaa48b718).
|
||||||
|
|
||||||
Open terminal and `cd` to the directory with source code. Configuration example for device with Conan:
|
Open terminal and `cd` to the directory with source code. Configuration example for device with Conan:
|
||||||
|
|
||||||
`cmake --preset ios-device-conan \`
|
```
|
||||||
` -D BUNDLE_IDENTIFIER_PREFIX=com.MY-NAME`
|
cmake --preset ios-device-conan \
|
||||||
|
-D BUNDLE_IDENTIFIER_PREFIX=com.MY-NAME
|
||||||
|
```
|
||||||
|
|
||||||
By default build directory containing Xcode project will appear at `../build-ios-device-conan`, but you can change it with `-B` option.
|
By default build directory containing Xcode project will appear at `../build-ios-device-conan`, but you can change it with `-B` option.
|
||||||
|
|
||||||
@ -53,7 +57,9 @@ You must also install game files, see [Installation on iOS](../players/Installat
|
|||||||
|
|
||||||
### From command line
|
### From command line
|
||||||
|
|
||||||
`cmake --build `<path to build directory>` --target vcmiclient -- -quiet`
|
```
|
||||||
|
cmake --build <path to build directory> --target vcmiclient -- -quiet
|
||||||
|
```
|
||||||
|
|
||||||
You can pass additional xcodebuild options after the `--`. Here `-quiet` is passed to reduce amount of output.
|
You can pass additional xcodebuild options after the `--`. Here `-quiet` is passed to reduce amount of output.
|
||||||
|
|
||||||
@ -67,4 +73,4 @@ Invoke `cpack` after building:
|
|||||||
|
|
||||||
`cpack -C Release`
|
`cpack -C Release`
|
||||||
|
|
||||||
This will generate file with extension **ipa** if you use CMake 3.25+and **zip** otherwise (simply change extension to ipa).
|
This will generate file with extension **ipa** if you use CMake 3.25+and **zip** otherwise (simply change extension to ipa).
|
||||||
|
@ -2,18 +2,20 @@
|
|||||||
|
|
||||||
# Requirements
|
# Requirements
|
||||||
|
|
||||||
1. C++ toolchain, either of:
|
1. C++ toolchain, either of:
|
||||||
- Xcode Command Line Tools (aka CLT): `sudo xcode-select --install`
|
- Xcode Command Line Tools (aka CLT): `sudo xcode-select --install`
|
||||||
- Xcode IDE: <https://developer.apple.com/xcode/>
|
- Xcode IDE: <https://developer.apple.com/xcode/>
|
||||||
- (not tested) other C++ compilers, e.g. gcc/clang from [Homebrew](https://brew.sh/)
|
- (not tested) other C++ compilers, e.g. gcc/clang from [Homebrew](https://brew.sh/)
|
||||||
2. CMake: `brew install --cask cmake` or get from <https://cmake.org/download/>
|
2. CMake: `brew install --cask cmake` or get from <https://cmake.org/download/>
|
||||||
3. (optional) Ninja: `brew install ninja` or get from <https://github.com/ninja-build/ninja/releases>
|
3. (optional) Ninja: `brew install ninja` or get from <https://github.com/ninja-build/ninja/releases>
|
||||||
|
|
||||||
# Obtaining source code
|
# Obtaining source code
|
||||||
|
|
||||||
Clone <https://github.com/vcmi/vcmi> with submodules. Example for command line:
|
Clone <https://github.com/vcmi/vcmi> with submodules. Example for command line:
|
||||||
|
|
||||||
`git clone --recurse-submodules https://github.com/vcmi/vcmi.git`
|
```
|
||||||
|
git clone --recurse-submodules https://github.com/vcmi/vcmi.git
|
||||||
|
```
|
||||||
|
|
||||||
# Obtaining dependencies
|
# Obtaining dependencies
|
||||||
|
|
||||||
@ -25,51 +27,48 @@ Please find detailed instructions in [VCMI repository](https://github.com/vcmi/v
|
|||||||
|
|
||||||
On the step where you need to replace **PROFILE**, choose:
|
On the step where you need to replace **PROFILE**, choose:
|
||||||
|
|
||||||
- if you're on an Intel Mac: `macos-intel`
|
- if you're on an Intel Mac: `macos-intel`
|
||||||
- if you're on an Apple Silicon Mac: `macos-arm`
|
- if you're on an Apple Silicon Mac: `macos-arm`
|
||||||
|
|
||||||
Note: if you wish to build 1.0 release in non-`Release` configuration, you should define `USE_CONAN_WITH_ALL_CONFIGS=1` environment variable when executing `conan install`.
|
Note: if you wish to build 1.0 release in non-`Release` configuration, you should define `USE_CONAN_WITH_ALL_CONFIGS=1` environment variable when executing `conan install`.
|
||||||
|
|
||||||
## Homebrew
|
## Homebrew
|
||||||
|
|
||||||
1. [Install Homebrew](https://brew.sh/)
|
1. [Install Homebrew](https://brew.sh/)
|
||||||
2. Install dependencies:
|
2. Install dependencies: `brew install boost minizip sdl2 sdl2_image sdl2_mixer sdl2_ttf tbb`
|
||||||
`brew install boost minizip sdl2 sdl2_image sdl2_mixer sdl2_ttf tbb`
|
3. If you want to watch in-game videos, also install FFmpeg: `brew install ffmpeg@4`
|
||||||
3. If you want to watch in-game videos, also install FFmpeg:
|
4. Install Qt dependency in either of the ways (note that you can skip this if you're not going to build Launcher):
|
||||||
`brew install ffmpeg@4`
|
- `brew install qt@5` for Qt 5 or `brew install qt` for Qt 6
|
||||||
4. Install Qt dependency in either of the ways (note that you can skip this if you're not going to build Launcher):
|
- using [Qt Online Installer](https://www.qt.io/download) - choose **Go open source**
|
||||||
- `brew install qt@5` for Qt 5 or `brew install qt` for Qt 6
|
|
||||||
- using [Qt Online Installer](https://www.qt.io/download) - choose **Go open source**
|
|
||||||
|
|
||||||
# Preparing build environment
|
# Preparing build environment
|
||||||
|
|
||||||
This applies only to Xcode-based toolchain. If `xcrun -f clang` prints errors, then use either of the following ways:
|
This applies only to Xcode-based toolchain. If `xcrun -f clang` prints errors, then use either of the following ways:
|
||||||
|
|
||||||
- select an Xcode instance from Xcode application - Preferences - Locations - Command Line Tools
|
- select an Xcode instance from Xcode application - Preferences - Locations - Command Line Tools
|
||||||
- use `xcode-select` utility to set Xcode or Xcode Command Line Tools path: for example,
|
- use `xcode-select` utility to set Xcode or Xcode Command Line Tools path: for example, `sudo xcode-select -s /Library/Developer/CommandLineTools`
|
||||||
`sudo xcode-select -s /Library/Developer/CommandLineTools`
|
- set `DEVELOPER_DIR` environment variable pointing to Xcode or Xcode Command Line Tools path: for example, `export DEVELOPER_DIR=/Applications/Xcode.app`
|
||||||
- set `DEVELOPER_DIR` environment variable pointing to Xcode or Xcode Command Line Tools path: for example, `export DEVELOPER_DIR=/Applications/Xcode.app`
|
|
||||||
|
|
||||||
# Configuring project for building
|
# Configuring project for building
|
||||||
|
|
||||||
Note that if you wish to use Qt Creator IDE, you should skip this step and configure respective variables inside the IDE.
|
Note that if you wish to use Qt Creator IDE, you should skip this step and configure respective variables inside the IDE.
|
||||||
|
|
||||||
1. In Terminal `cd` to the source code directory
|
1. In Terminal `cd` to the source code directory
|
||||||
2. Start assembling CMake invocation: type `cmake -S . -B BUILD_DIR` where *BUILD_DIR* can be any path, **don't press Return**
|
2. Start assembling CMake invocation: type `cmake -S . -B BUILD_DIR` where *BUILD_DIR* can be any path, **don't press Return**
|
||||||
3. Decide which CMake generator you want to use:
|
3. Decide which CMake generator you want to use:
|
||||||
- Makefiles: no extra option needed or pass `-G 'Unix Makefiles'`
|
- Makefiles: no extra option needed or pass `-G 'Unix Makefiles'`
|
||||||
- Ninja (if you have installed it): pass `-G Ninja`
|
- Ninja (if you have installed it): pass `-G Ninja`
|
||||||
- Xcode IDE (if you have installed it): pass `-G Xcode`
|
- Xcode IDE (if you have installed it): pass `-G Xcode`
|
||||||
4. If you picked Makefiles or Ninja, pick desired *build type* - either of Debug / RelWithDebInfo / Release / MinSizeRel - and pass it in `CMAKE_BUILD_TYPE` option, for example: `-D CMAKE_BUILD_TYPE=Release`. If you don't pass this option, `RelWithDebInfo` will be used.
|
4. If you picked Makefiles or Ninja, pick desired *build type* - either of Debug / RelWithDebInfo / Release / MinSizeRel - and pass it in `CMAKE_BUILD_TYPE` option, for example: `-D CMAKE_BUILD_TYPE=Release`. If you don't pass this option, `RelWithDebInfo` will be used.
|
||||||
5. If you don't want to build Launcher, pass `-D ENABLE_LAUNCHER=OFF`
|
5. If you don't want to build Launcher, pass `-D ENABLE_LAUNCHER=OFF`
|
||||||
6. You can also pass `-Wno-dev` if you're not interested in CMake developer warnings
|
6. You can also pass `-Wno-dev` if you're not interested in CMake developer warnings
|
||||||
7. Next step depends on the dependency manager you have picked:
|
7. Next step depends on the dependency manager you have picked:
|
||||||
- Conan: pass `-D CMAKE_TOOLCHAIN_FILE=conan-generated/conan_toolchain.cmake` where **conan-generated** must be replaced with your directory choice
|
- Conan: pass `-D CMAKE_TOOLCHAIN_FILE=conan-generated/conan_toolchain.cmake` where **conan-generated** must be replaced with your directory choice
|
||||||
- Homebrew: if you installed FFmpeg or Qt 5, you need to pass `-D "CMAKE_PREFIX_PATH="` variable. See below what you can insert after `=` (but **before the closing quote**), multiple values must be separated with `;` (semicolon):
|
- Homebrew: if you installed FFmpeg or Qt 5, you need to pass `-D "CMAKE_PREFIX_PATH="` variable. See below what you can insert after `=` (but **before the closing quote**), multiple values must be separated with `;` (semicolon):
|
||||||
- if you installed FFmpeg, insert `$(brew --prefix ffmpeg@4)`
|
- if you installed FFmpeg, insert `$(brew --prefix ffmpeg@4)`
|
||||||
- if you installed Qt 5 from Homebrew, insert:`$(brew --prefix qt@5)`
|
- if you installed Qt 5 from Homebrew, insert:`$(brew --prefix qt@5)`
|
||||||
- if you installed Qt from Online Installer, insert your path to Qt directory, for example: `/Users/kambala/dev/Qt-libs/5.15.2/Clang64`
|
- if you installed Qt from Online Installer, insert your path to Qt directory, for example: `/Users/kambala/dev/Qt-libs/5.15.2/Clang64`
|
||||||
- example for FFmpeg + Qt 5: `-D "CMAKE_PREFIX_PATH=$(brew --prefix ffmpeg@4);$(brew --prefix qt@5)"`
|
- example for FFmpeg + Qt 5: `-D "CMAKE_PREFIX_PATH=$(brew --prefix ffmpeg@4);$(brew --prefix qt@5)"`
|
||||||
8. now press Return
|
8. now press Return
|
||||||
|
|
||||||
# Building project
|
# Building project
|
||||||
@ -82,10 +81,10 @@ Open `VCMI.xcodeproj` from the build directory, select `vcmiclient` scheme and h
|
|||||||
|
|
||||||
## From command line
|
## From command line
|
||||||
|
|
||||||
`cmake --build `<path to build directory>
|
`cmake --build <path to build directory>`
|
||||||
|
|
||||||
- If using Makefiles generator, you'd want to utilize all your CPU cores by appending `-- -j$(sysctl -n hw.ncpu)` to the above
|
- If using Makefiles generator, you'd want to utilize all your CPU cores by appending `-- -j$(sysctl -n hw.ncpu)` to the above
|
||||||
- If using Xcode generator, you can also choose which configuration to build by appending `--config `<configuration name> to the above, for example: `--config Debug`
|
- If using Xcode generator, you can also choose which configuration to build by appending `--config <configuration name>` to the above, for example: `--config Debug`
|
||||||
|
|
||||||
# Packaging project into DMG file
|
# Packaging project into DMG file
|
||||||
|
|
||||||
@ -97,27 +96,23 @@ If you use Conan, it's expected that you use **conan-generated** directory at st
|
|||||||
|
|
||||||
You can run VCMI from DMG, but it's will also work from your IDE be it Xcode or Qt Creator.
|
You can run VCMI from DMG, but it's will also work from your IDE be it Xcode or Qt Creator.
|
||||||
|
|
||||||
Alternatively you can run binaries directly from "bin" directory:
|
Alternatively you can run binaries directly from the **bin** directory:
|
||||||
|
|
||||||
BUILD_DIR/bin/vcmilauncher
|
- BUILD_DIR/bin/vcmilauncher
|
||||||
BUILD_DIR/bin/vcmiclient
|
- BUILD_DIR/bin/vcmiclient
|
||||||
BUILD_DIR/bin/vcmiserver
|
- BUILD_DIR/bin/vcmiserver
|
||||||
|
|
||||||
CMake include commands to copy all needed assets from source directory into "bin" on each build. They'll work when you build from Xcode too.
|
CMake include commands to copy all needed assets from source directory into the **bin** directory on each build. They'll work when you build from Xcode too.
|
||||||
|
|
||||||
# Some useful debugging tips
|
# Some useful debugging tips
|
||||||
|
|
||||||
Anyone who might want to debug builds, but new to macOS could find following commands useful:
|
Anyone who might want to debug builds, but new to macOS could find following commands useful:
|
||||||
|
|
||||||
- To attach DMG file from command line use
|
- To attach DMG file from command line use `hdiutil attach vcmi-1.0.dmg`
|
||||||
`hdiutil attach vcmi-1.0.dmg`
|
- Detach volume: `hdiutil detach /Volumes/vcmi-1.0`
|
||||||
- Detach volume:
|
- To view dependency paths: `otool -L /Volumes/vcmi-1.0/VCMI.app/Contents/MacOS/vcmiclient`
|
||||||
`hdiutil detach /Volumes/vcmi-1.0`
|
- To display load commands such as `LC_RPATH`: `otool -l /Volumes/vcmi-1.0/VCMI.app/Contents/MacOS/vcmiclient`
|
||||||
- To view dependency paths
|
|
||||||
`otool -L /Volumes/vcmi-1.0/VCMI.app/Contents/MacOS/vcmiclient`
|
|
||||||
- To display load commands such as LC_RPATH
|
|
||||||
`otool -l /Volumes/vcmi-1.0/VCMI.app/Contents/MacOS/vcmiclient`
|
|
||||||
|
|
||||||
# Troubleshooting
|
# Troubleshooting
|
||||||
|
|
||||||
In case of troubles you can always consult our CI build scripts or contact the dev team via slack
|
In case of troubles you can always consult our CI build scripts or contact the dev team via slack.
|
||||||
|
@ -83,6 +83,8 @@ void CSettingsView::loadSettings()
|
|||||||
|
|
||||||
ui->spinBoxInterfaceScaling->setValue(settings["video"]["resolution"]["scaling"].Float());
|
ui->spinBoxInterfaceScaling->setValue(settings["video"]["resolution"]["scaling"].Float());
|
||||||
ui->spinBoxFramerateLimit->setValue(settings["video"]["targetfps"].Float());
|
ui->spinBoxFramerateLimit->setValue(settings["video"]["targetfps"].Float());
|
||||||
|
ui->spinBoxFramerateLimit->setDisabled(settings["video"]["vsync"].Bool());
|
||||||
|
ui->checkBoxVSync->setChecked(settings["video"]["vsync"].Bool());
|
||||||
ui->spinBoxReservedArea->setValue(std::round(settings["video"]["reservedWidth"].Float() * 100));
|
ui->spinBoxReservedArea->setValue(std::round(settings["video"]["reservedWidth"].Float() * 100));
|
||||||
|
|
||||||
ui->comboBoxFriendlyAI->setCurrentText(QString::fromStdString(settings["server"]["friendlyAI"].String()));
|
ui->comboBoxFriendlyAI->setCurrentText(QString::fromStdString(settings["server"]["friendlyAI"].String()));
|
||||||
@ -494,6 +496,13 @@ void CSettingsView::on_spinBoxFramerateLimit_valueChanged(int arg1)
|
|||||||
node->Float() = arg1;
|
node->Float() = arg1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CSettingsView::on_checkBoxVSync_stateChanged(int arg1)
|
||||||
|
{
|
||||||
|
Settings node = settings.write["video"]["vsync"];
|
||||||
|
node->Bool() = arg1;
|
||||||
|
ui->spinBoxFramerateLimit->setDisabled(settings["video"]["vsync"].Bool());
|
||||||
|
}
|
||||||
|
|
||||||
void CSettingsView::on_comboBoxEnemyPlayerAI_currentTextChanged(const QString &arg1)
|
void CSettingsView::on_comboBoxEnemyPlayerAI_currentTextChanged(const QString &arg1)
|
||||||
{
|
{
|
||||||
Settings node = settings.write["server"]["playerAI"];
|
Settings node = settings.write["server"]["playerAI"];
|
||||||
|
@ -62,6 +62,8 @@ private slots:
|
|||||||
|
|
||||||
void on_spinBoxFramerateLimit_valueChanged(int arg1);
|
void on_spinBoxFramerateLimit_valueChanged(int arg1);
|
||||||
|
|
||||||
|
void on_checkBoxVSync_stateChanged(int arg1);
|
||||||
|
|
||||||
void on_comboBoxEnemyPlayerAI_currentTextChanged(const QString &arg1);
|
void on_comboBoxEnemyPlayerAI_currentTextChanged(const QString &arg1);
|
||||||
|
|
||||||
void on_comboBoxAlliedPlayerAI_currentTextChanged(const QString &arg1);
|
void on_comboBoxAlliedPlayerAI_currentTextChanged(const QString &arg1);
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -115,7 +115,7 @@ DLL_LINKAGE bool ArtifactUtils::isBackpackFreeSlots(const CArtifactSet * target,
|
|||||||
}
|
}
|
||||||
|
|
||||||
DLL_LINKAGE std::vector<const CArtifact*> ArtifactUtils::assemblyPossibilities(
|
DLL_LINKAGE std::vector<const CArtifact*> ArtifactUtils::assemblyPossibilities(
|
||||||
const CArtifactSet * artSet, const ArtifactID & aid, bool equipped)
|
const CArtifactSet * artSet, const ArtifactID & aid)
|
||||||
{
|
{
|
||||||
std::vector<const CArtifact*> arts;
|
std::vector<const CArtifact*> arts;
|
||||||
const auto * art = aid.toArtifact();
|
const auto * art = aid.toArtifact();
|
||||||
@ -129,23 +129,10 @@ DLL_LINKAGE std::vector<const CArtifact*> ArtifactUtils::assemblyPossibilities(
|
|||||||
|
|
||||||
for(const auto constituent : artifact->getConstituents()) //check if all constituents are available
|
for(const auto constituent : artifact->getConstituents()) //check if all constituents are available
|
||||||
{
|
{
|
||||||
if(equipped)
|
if(!artSet->hasArt(constituent->getId(), false, false, false))
|
||||||
{
|
{
|
||||||
// Search for equipped arts
|
possible = false;
|
||||||
if(!artSet->hasArt(constituent->getId(), true, false, false))
|
break;
|
||||||
{
|
|
||||||
possible = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Search in backpack
|
|
||||||
if(!artSet->hasArtBackpack(constituent->getId()))
|
|
||||||
{
|
|
||||||
possible = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(possible)
|
if(possible)
|
||||||
|
@ -36,7 +36,7 @@ namespace ArtifactUtils
|
|||||||
DLL_LINKAGE bool isSlotBackpack(const ArtifactPosition & slot);
|
DLL_LINKAGE bool isSlotBackpack(const ArtifactPosition & slot);
|
||||||
DLL_LINKAGE bool isSlotEquipment(const ArtifactPosition & slot);
|
DLL_LINKAGE bool isSlotEquipment(const ArtifactPosition & slot);
|
||||||
DLL_LINKAGE bool isBackpackFreeSlots(const CArtifactSet * target, const size_t reqSlots = 1);
|
DLL_LINKAGE bool isBackpackFreeSlots(const CArtifactSet * target, const size_t reqSlots = 1);
|
||||||
DLL_LINKAGE std::vector<const CArtifact*> assemblyPossibilities(const CArtifactSet * artSet, const ArtifactID & aid, bool equipped);
|
DLL_LINKAGE std::vector<const CArtifact*> assemblyPossibilities(const CArtifactSet * artSet, const ArtifactID & aid);
|
||||||
DLL_LINKAGE CArtifactInstance * createScroll(const SpellID & sid);
|
DLL_LINKAGE CArtifactInstance * createScroll(const SpellID & sid);
|
||||||
DLL_LINKAGE CArtifactInstance * createNewArtifactInstance(CArtifact * art);
|
DLL_LINKAGE CArtifactInstance * createNewArtifactInstance(CArtifact * art);
|
||||||
DLL_LINKAGE CArtifactInstance * createNewArtifactInstance(const ArtifactID & aid);
|
DLL_LINKAGE CArtifactInstance * createNewArtifactInstance(const ArtifactID & aid);
|
||||||
|
@ -819,12 +819,6 @@ ArtifactPosition CArtifactSet::getArtPos(const ArtifactID & aid, bool onlyWorn,
|
|||||||
return result.empty() ? ArtifactPosition{ArtifactPosition::PRE_FIRST} : result[0];
|
return result.empty() ? ArtifactPosition{ArtifactPosition::PRE_FIRST} : result[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
ArtifactPosition CArtifactSet::getArtBackpackPos(const ArtifactID & aid) const
|
|
||||||
{
|
|
||||||
const auto result = getBackpackArtPositions(aid);
|
|
||||||
return result.empty() ? ArtifactPosition{ArtifactPosition::PRE_FIRST} : result[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<ArtifactPosition> CArtifactSet::getAllArtPositions(const ArtifactID & aid, bool onlyWorn, bool allowLocked, bool getAll) const
|
std::vector<ArtifactPosition> CArtifactSet::getAllArtPositions(const ArtifactID & aid, bool onlyWorn, bool allowLocked, bool getAll) const
|
||||||
{
|
{
|
||||||
std::vector<ArtifactPosition> result;
|
std::vector<ArtifactPosition> result;
|
||||||
|
@ -262,7 +262,6 @@ public:
|
|||||||
/// (if more than one such artifact lower ID is returned)
|
/// (if more than one such artifact lower ID is returned)
|
||||||
ArtifactPosition getArtPos(const ArtifactID & aid, bool onlyWorn = true, bool allowLocked = true) const;
|
ArtifactPosition getArtPos(const ArtifactID & aid, bool onlyWorn = true, bool allowLocked = true) const;
|
||||||
ArtifactPosition getArtPos(const CArtifactInstance *art) const;
|
ArtifactPosition getArtPos(const CArtifactInstance *art) const;
|
||||||
ArtifactPosition getArtBackpackPos(const ArtifactID & aid) const;
|
|
||||||
std::vector<ArtifactPosition> getAllArtPositions(const ArtifactID & aid, bool onlyWorn, bool allowLocked, bool getAll) const;
|
std::vector<ArtifactPosition> getAllArtPositions(const ArtifactID & aid, bool onlyWorn, bool allowLocked, bool getAll) const;
|
||||||
std::vector<ArtifactPosition> getBackpackArtPositions(const ArtifactID & aid) const;
|
std::vector<ArtifactPosition> getBackpackArtPositions(const ArtifactID & aid) const;
|
||||||
const CArtifactInstance * getArtByInstanceId(const ArtifactInstanceID & artInstId) const;
|
const CArtifactInstance * getArtByInstanceId(const ArtifactInstanceID & artInstId) const;
|
||||||
|
@ -649,33 +649,34 @@ EPlayerStatus CGameInfoCallback::getPlayerStatus(PlayerColor player, bool verbos
|
|||||||
|
|
||||||
std::string CGameInfoCallback::getTavernRumor(const CGObjectInstance * townOrTavern) const
|
std::string CGameInfoCallback::getTavernRumor(const CGObjectInstance * townOrTavern) const
|
||||||
{
|
{
|
||||||
std::string text;
|
MetaString text;
|
||||||
|
text.appendLocalString(EMetaText::GENERAL_TXT, 216);
|
||||||
|
|
||||||
std::string extraText;
|
std::string extraText;
|
||||||
if(gs->rumor.type == RumorState::TYPE_NONE)
|
if(gs->rumor.type == RumorState::TYPE_NONE)
|
||||||
return text;
|
return text.toString();
|
||||||
|
|
||||||
auto rumor = gs->rumor.last[gs->rumor.type];
|
auto rumor = gs->rumor.last[gs->rumor.type];
|
||||||
switch(gs->rumor.type)
|
switch(gs->rumor.type)
|
||||||
{
|
{
|
||||||
case RumorState::TYPE_SPECIAL:
|
case RumorState::TYPE_SPECIAL:
|
||||||
|
text.replaceLocalString(EMetaText::GENERAL_TXT, rumor.first);
|
||||||
if(rumor.first == RumorState::RUMOR_GRAIL)
|
if(rumor.first == RumorState::RUMOR_GRAIL)
|
||||||
extraText = VLC->generaltexth->arraytxt[158 + rumor.second];
|
text.replaceTextID(TextIdentifier("core", "genrltxt", "arraytxt", 158 + rumor.second).get());
|
||||||
else
|
else
|
||||||
extraText = VLC->generaltexth->capColors[rumor.second];
|
text.replaceTextID(TextIdentifier("core", "genrltxt", "capitalColors", rumor.second).get());
|
||||||
|
|
||||||
text = boost::str(boost::format(VLC->generaltexth->allTexts[rumor.first]) % extraText);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case RumorState::TYPE_MAP:
|
case RumorState::TYPE_MAP:
|
||||||
text = gs->map->rumors[rumor.first].text;
|
text.replaceRawString(gs->map->rumors[rumor.first].text.toString());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RumorState::TYPE_RAND:
|
case RumorState::TYPE_RAND:
|
||||||
text = VLC->generaltexth->tavernRumors[rumor.first];
|
text.replaceTextID(TextIdentifier("core", "genrltxt", "randtvrn", rumor.first).get());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return text;
|
return text.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerRelations CGameInfoCallback::getPlayerRelations( PlayerColor color1, PlayerColor color2 ) const
|
PlayerRelations CGameInfoCallback::getPlayerRelations( PlayerColor color1, PlayerColor color2 ) const
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "CGeneralTextHandler.h"
|
#include "CGeneralTextHandler.h"
|
||||||
|
|
||||||
#include "filesystem/Filesystem.h"
|
#include "filesystem/Filesystem.h"
|
||||||
|
#include "serializer/JsonSerializeFormat.h"
|
||||||
#include "CConfigHandler.h"
|
#include "CConfigHandler.h"
|
||||||
#include "GameSettings.h"
|
#include "GameSettings.h"
|
||||||
#include "mapObjects/CQuest.h"
|
#include "mapObjects/CQuest.h"
|
||||||
@ -247,67 +248,7 @@ bool CLegacyConfigParser::endLine()
|
|||||||
return curr < end;
|
return curr < end;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGeneralTextHandler::readToVector(const std::string & sourceID, const std::string & sourceName)
|
void TextLocalizationContainer::registerStringOverride(const std::string & modContext, const std::string & language, const TextIdentifier & UID, const std::string & localized)
|
||||||
{
|
|
||||||
CLegacyConfigParser parser(TextPath::builtin(sourceName));
|
|
||||||
size_t index = 0;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
registerString( "core", {sourceID, index}, parser.readString());
|
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
while (parser.endLine());
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string & CGeneralTextHandler::deserialize(const TextIdentifier & identifier) const
|
|
||||||
{
|
|
||||||
if(stringsLocalizations.count(identifier.get()) == 0)
|
|
||||||
{
|
|
||||||
logGlobal->error("Unable to find localization for string '%s'", identifier.get());
|
|
||||||
return identifier.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto & entry = stringsLocalizations.at(identifier.get());
|
|
||||||
|
|
||||||
if (!entry.overrideValue.empty())
|
|
||||||
return entry.overrideValue;
|
|
||||||
return entry.baseValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CGeneralTextHandler::registerString(const std::string & modContext, const TextIdentifier & UID, const std::string & localized)
|
|
||||||
{
|
|
||||||
assert(!modContext.empty());
|
|
||||||
assert(!getModLanguage(modContext).empty());
|
|
||||||
assert(UID.get().find("..") == std::string::npos); // invalid identifier - there is section that was evaluated to empty string
|
|
||||||
//assert(stringsLocalizations.count(UID.get()) == 0); // registering already registered string?
|
|
||||||
|
|
||||||
if(stringsLocalizations.count(UID.get()) > 0)
|
|
||||||
{
|
|
||||||
auto & value = stringsLocalizations[UID.get()];
|
|
||||||
|
|
||||||
if(value.baseLanguage.empty())
|
|
||||||
{
|
|
||||||
value.baseLanguage = getModLanguage(modContext);
|
|
||||||
value.baseValue = localized;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(value.baseValue != localized)
|
|
||||||
logMod->warn("Duplicate registered string '%s' found! Old value: '%s', new value: '%s'", UID.get(), value.baseValue, localized);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
StringState result;
|
|
||||||
result.baseLanguage = getModLanguage(modContext);
|
|
||||||
result.baseValue = localized;
|
|
||||||
result.modContext = modContext;
|
|
||||||
|
|
||||||
stringsLocalizations[UID.get()] = result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CGeneralTextHandler::registerStringOverride(const std::string & modContext, const std::string & language, const TextIdentifier & UID, const std::string & localized)
|
|
||||||
{
|
{
|
||||||
assert(!modContext.empty());
|
assert(!modContext.empty());
|
||||||
assert(!language.empty());
|
assert(!language.empty());
|
||||||
@ -321,7 +262,66 @@ void CGeneralTextHandler::registerStringOverride(const std::string & modContext,
|
|||||||
entry.modContext = modContext;
|
entry.modContext = modContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CGeneralTextHandler::validateTranslation(const std::string & language, const std::string & modContext, const JsonNode & config) const
|
void TextLocalizationContainer::addSubContainer(const TextLocalizationContainer & container)
|
||||||
|
{
|
||||||
|
subContainers.insert(&container);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextLocalizationContainer::removeSubContainer(const TextLocalizationContainer & container)
|
||||||
|
{
|
||||||
|
subContainers.erase(&container);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string & TextLocalizationContainer::deserialize(const TextIdentifier & identifier) const
|
||||||
|
{
|
||||||
|
if(stringsLocalizations.count(identifier.get()) == 0)
|
||||||
|
{
|
||||||
|
for(const auto * container : subContainers)
|
||||||
|
if(container->identifierExists(identifier))
|
||||||
|
return container->deserialize(identifier);
|
||||||
|
|
||||||
|
logGlobal->error("Unable to find localization for string '%s'", identifier.get());
|
||||||
|
return identifier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto & entry = stringsLocalizations.at(identifier.get());
|
||||||
|
|
||||||
|
if (!entry.overrideValue.empty())
|
||||||
|
return entry.overrideValue;
|
||||||
|
return entry.baseValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextLocalizationContainer::registerString(const std::string & modContext, const TextIdentifier & UID, const std::string & localized, const std::string & language)
|
||||||
|
{
|
||||||
|
assert(!modContext.empty());
|
||||||
|
assert(!Languages::getLanguageOptions(language).identifier.empty());
|
||||||
|
assert(UID.get().find("..") == std::string::npos); // invalid identifier - there is section that was evaluated to empty string
|
||||||
|
//assert(stringsLocalizations.count(UID.get()) == 0); // registering already registered string?
|
||||||
|
|
||||||
|
if(stringsLocalizations.count(UID.get()) > 0)
|
||||||
|
{
|
||||||
|
auto & value = stringsLocalizations[UID.get()];
|
||||||
|
value.baseLanguage = language;
|
||||||
|
value.baseValue = localized;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StringState value;
|
||||||
|
value.baseLanguage = language;
|
||||||
|
value.baseValue = localized;
|
||||||
|
value.modContext = modContext;
|
||||||
|
|
||||||
|
stringsLocalizations[UID.get()] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextLocalizationContainer::registerString(const std::string & modContext, const TextIdentifier & UID, const std::string & localized)
|
||||||
|
{
|
||||||
|
assert(!getModLanguage(modContext).empty());
|
||||||
|
registerString(modContext, UID, localized, getModLanguage(modContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextLocalizationContainer::validateTranslation(const std::string & language, const std::string & modContext, const JsonNode & config) const
|
||||||
{
|
{
|
||||||
bool allPresent = true;
|
bool allPresent = true;
|
||||||
|
|
||||||
@ -372,12 +372,60 @@ bool CGeneralTextHandler::validateTranslation(const std::string & language, cons
|
|||||||
return allPresent && allFound;
|
return allPresent && allFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGeneralTextHandler::loadTranslationOverrides(const std::string & language, const std::string & modContext, const JsonNode & config)
|
void TextLocalizationContainer::loadTranslationOverrides(const std::string & language, const std::string & modContext, const JsonNode & config)
|
||||||
{
|
{
|
||||||
for(const auto & node : config.Struct())
|
for(const auto & node : config.Struct())
|
||||||
registerStringOverride(modContext, language, node.first, node.second.String());
|
registerStringOverride(modContext, language, node.first, node.second.String());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TextLocalizationContainer::identifierExists(const TextIdentifier & UID) const
|
||||||
|
{
|
||||||
|
return stringsLocalizations.count(UID.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextLocalizationContainer::dumpAllTexts()
|
||||||
|
{
|
||||||
|
logGlobal->info("BEGIN TEXT EXPORT");
|
||||||
|
for(const auto & entry : stringsLocalizations)
|
||||||
|
{
|
||||||
|
if (!entry.second.overrideValue.empty())
|
||||||
|
logGlobal->info(R"("%s" : "%s",)", entry.first, TextOperations::escapeString(entry.second.overrideValue));
|
||||||
|
else
|
||||||
|
logGlobal->info(R"("%s" : "%s",)", entry.first, TextOperations::escapeString(entry.second.baseValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
logGlobal->info("END TEXT EXPORT");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string TextLocalizationContainer::getModLanguage(const std::string & modContext)
|
||||||
|
{
|
||||||
|
if (modContext == "core")
|
||||||
|
return CGeneralTextHandler::getInstalledLanguage();
|
||||||
|
return VLC->modh->getModLanguage(modContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextLocalizationContainer::jsonSerialize(JsonNode & dest) const
|
||||||
|
{
|
||||||
|
for(auto & s : stringsLocalizations)
|
||||||
|
{
|
||||||
|
dest.Struct()[s.first].String() = s.second.baseValue;
|
||||||
|
if(!s.second.overrideValue.empty())
|
||||||
|
dest.Struct()[s.first].String() = s.second.overrideValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGeneralTextHandler::readToVector(const std::string & sourceID, const std::string & sourceName)
|
||||||
|
{
|
||||||
|
CLegacyConfigParser parser(TextPath::builtin(sourceName));
|
||||||
|
size_t index = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
registerString( "core", {sourceID, index}, parser.readString());
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
while (parser.endLine());
|
||||||
|
}
|
||||||
|
|
||||||
CGeneralTextHandler::CGeneralTextHandler():
|
CGeneralTextHandler::CGeneralTextHandler():
|
||||||
victoryConditions(*this, "core.vcdesc" ),
|
victoryConditions(*this, "core.vcdesc" ),
|
||||||
lossCondtions (*this, "core.lcdesc" ),
|
lossCondtions (*this, "core.lcdesc" ),
|
||||||
@ -591,20 +639,6 @@ int32_t CGeneralTextHandler::pluralText(const int32_t textIndex, const int32_t c
|
|||||||
return textIndex + 1;
|
return textIndex + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGeneralTextHandler::dumpAllTexts()
|
|
||||||
{
|
|
||||||
logGlobal->info("BEGIN TEXT EXPORT");
|
|
||||||
for(const auto & entry : stringsLocalizations)
|
|
||||||
{
|
|
||||||
if (!entry.second.overrideValue.empty())
|
|
||||||
logGlobal->info(R"("%s" : "%s",)", entry.first, TextOperations::escapeString(entry.second.overrideValue));
|
|
||||||
else
|
|
||||||
logGlobal->info(R"("%s" : "%s",)", entry.first, TextOperations::escapeString(entry.second.baseValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
logGlobal->info("END TEXT EXPORT");
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t CGeneralTextHandler::getCampaignLength(size_t campaignID) const
|
size_t CGeneralTextHandler::getCampaignLength(size_t campaignID) const
|
||||||
{
|
{
|
||||||
assert(campaignID < scenariosCountPerCampaign.size());
|
assert(campaignID < scenariosCountPerCampaign.size());
|
||||||
@ -614,13 +648,6 @@ size_t CGeneralTextHandler::getCampaignLength(size_t campaignID) const
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CGeneralTextHandler::getModLanguage(const std::string & modContext)
|
|
||||||
{
|
|
||||||
if (modContext == "core")
|
|
||||||
return getInstalledLanguage();
|
|
||||||
return VLC->modh->getModLanguage(modContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string CGeneralTextHandler::getPreferredLanguage()
|
std::string CGeneralTextHandler::getPreferredLanguage()
|
||||||
{
|
{
|
||||||
assert(!settings["general"]["language"].String().empty());
|
assert(!settings["general"]["language"].String().empty());
|
||||||
|
@ -15,6 +15,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
|||||||
|
|
||||||
class CInputStream;
|
class CInputStream;
|
||||||
class JsonNode;
|
class JsonNode;
|
||||||
|
class JsonSerializeFormat;
|
||||||
|
|
||||||
/// Parser for any text files from H3
|
/// Parser for any text files from H3
|
||||||
class DLL_LINKAGE CLegacyConfigParser
|
class DLL_LINKAGE CLegacyConfigParser
|
||||||
@ -113,9 +114,9 @@ public:
|
|||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Handles all text-related data in game
|
class DLL_LINKAGE TextLocalizationContainer
|
||||||
class DLL_LINKAGE CGeneralTextHandler
|
|
||||||
{
|
{
|
||||||
|
protected:
|
||||||
struct StringState
|
struct StringState
|
||||||
{
|
{
|
||||||
/// Human-readable string that was added on registration
|
/// Human-readable string that was added on registration
|
||||||
@ -132,21 +133,26 @@ class DLL_LINKAGE CGeneralTextHandler
|
|||||||
|
|
||||||
/// ID of mod that created this string
|
/// ID of mod that created this string
|
||||||
std::string modContext;
|
std::string modContext;
|
||||||
|
|
||||||
|
template <typename Handler>
|
||||||
|
void serialize(Handler & h, const int Version)
|
||||||
|
{
|
||||||
|
h & baseValue;
|
||||||
|
h & baseLanguage;
|
||||||
|
h & modContext;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// map identifier -> localization
|
/// map identifier -> localization
|
||||||
std::unordered_map<std::string, StringState> stringsLocalizations;
|
std::unordered_map<std::string, StringState> stringsLocalizations;
|
||||||
|
|
||||||
void readToVector(const std::string & sourceID, const std::string & sourceName);
|
std::set<const TextLocalizationContainer *> subContainers;
|
||||||
|
|
||||||
/// number of scenarios in specific campaign. TODO: move to a better location
|
|
||||||
std::vector<size_t> scenariosCountPerCampaign;
|
|
||||||
|
|
||||||
std::string getModLanguage(const std::string & modContext);
|
|
||||||
|
|
||||||
/// add selected string to internal storage as high-priority strings
|
/// add selected string to internal storage as high-priority strings
|
||||||
void registerStringOverride(const std::string & modContext, const std::string & language, const TextIdentifier & UID, const std::string & localized);
|
void registerStringOverride(const std::string & modContext, const std::string & language, const TextIdentifier & UID, const std::string & localized);
|
||||||
|
|
||||||
|
std::string getModLanguage(const std::string & modContext);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// validates translation of specified language for specified mod
|
/// validates translation of specified language for specified mod
|
||||||
/// returns true if localization is valid and complete
|
/// returns true if localization is valid and complete
|
||||||
@ -157,13 +163,13 @@ public:
|
|||||||
/// Any entries loaded by this will have priority over texts registered normally
|
/// Any entries loaded by this will have priority over texts registered normally
|
||||||
void loadTranslationOverrides(const std::string & language, const std::string & modContext, JsonNode const & file);
|
void loadTranslationOverrides(const std::string & language, const std::string & modContext, JsonNode const & file);
|
||||||
|
|
||||||
|
// returns true if identifier with such name was registered, even if not translated to current language
|
||||||
|
bool identifierExists(const TextIdentifier & UID) const;
|
||||||
|
|
||||||
/// add selected string to internal storage
|
/// add selected string to internal storage
|
||||||
void registerString(const std::string & modContext, const TextIdentifier & UID, const std::string & localized);
|
void registerString(const std::string & modContext, const TextIdentifier & UID, const std::string & localized);
|
||||||
|
void registerString(const std::string & modContext, const TextIdentifier & UID, const std::string & localized, const std::string & language);
|
||||||
// returns true if identifier with such name was registered, even if not translated to current language
|
|
||||||
// not required right now, can be added if necessary
|
|
||||||
// bool identifierExists( const std::string identifier) const;
|
|
||||||
|
|
||||||
/// returns translated version of a string that can be displayed to user
|
/// returns translated version of a string that can be displayed to user
|
||||||
template<typename ... Args>
|
template<typename ... Args>
|
||||||
std::string translate(std::string arg1, Args ... args) const
|
std::string translate(std::string arg1, Args ... args) const
|
||||||
@ -174,10 +180,53 @@ public:
|
|||||||
|
|
||||||
/// converts identifier into user-readable string
|
/// converts identifier into user-readable string
|
||||||
const std::string & deserialize(const TextIdentifier & identifier) const;
|
const std::string & deserialize(const TextIdentifier & identifier) const;
|
||||||
|
|
||||||
/// Debug method, dumps all currently known texts into console using Json-like format
|
/// Debug method, dumps all currently known texts into console using Json-like format
|
||||||
void dumpAllTexts();
|
void dumpAllTexts();
|
||||||
|
|
||||||
|
/// Add or override subcontainer which can store identifiers
|
||||||
|
void addSubContainer(const TextLocalizationContainer & container);
|
||||||
|
|
||||||
|
/// Remove subcontainer with give name
|
||||||
|
void removeSubContainer(const TextLocalizationContainer & container);
|
||||||
|
|
||||||
|
void jsonSerialize(JsonNode & dest) const;
|
||||||
|
|
||||||
|
template <typename Handler>
|
||||||
|
void serialize(Handler & h, const int Version)
|
||||||
|
{
|
||||||
|
std::string key;
|
||||||
|
auto sz = stringsLocalizations.size();
|
||||||
|
h & sz;
|
||||||
|
if(h.saving)
|
||||||
|
{
|
||||||
|
for(auto s : stringsLocalizations)
|
||||||
|
{
|
||||||
|
key = s.first;
|
||||||
|
h & key;
|
||||||
|
h & s.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(size_t i = 0; i < sz; ++i)
|
||||||
|
{
|
||||||
|
h & key;
|
||||||
|
h & stringsLocalizations[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Handles all text-related data in game
|
||||||
|
class DLL_LINKAGE CGeneralTextHandler: public TextLocalizationContainer
|
||||||
|
{
|
||||||
|
void readToVector(const std::string & sourceID, const std::string & sourceName);
|
||||||
|
|
||||||
|
/// number of scenarios in specific campaign. TODO: move to a better location
|
||||||
|
std::vector<size_t> scenariosCountPerCampaign;
|
||||||
|
|
||||||
|
public:
|
||||||
LegacyTextContainer allTexts;
|
LegacyTextContainer allTexts;
|
||||||
|
|
||||||
LegacyTextContainer arraytxt;
|
LegacyTextContainer arraytxt;
|
||||||
|
@ -20,7 +20,6 @@ struct SetMovePoints;
|
|||||||
struct GiveBonus;
|
struct GiveBonus;
|
||||||
struct BlockingDialog;
|
struct BlockingDialog;
|
||||||
struct TeleportDialog;
|
struct TeleportDialog;
|
||||||
class MetaString;
|
|
||||||
struct StackLocation;
|
struct StackLocation;
|
||||||
struct ArtifactLocation;
|
struct ArtifactLocation;
|
||||||
class CCreatureSet;
|
class CCreatureSet;
|
||||||
|
@ -102,7 +102,7 @@ void MetaString::clear()
|
|||||||
|
|
||||||
bool MetaString::empty() const
|
bool MetaString::empty() const
|
||||||
{
|
{
|
||||||
return message.empty();
|
return message.empty() || toString().empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string MetaString::getLocalString(const std::pair<EMetaText, ui32> & txt) const
|
std::string MetaString::getLocalString(const std::pair<EMetaText, ui32> & txt) const
|
||||||
|
@ -1920,43 +1920,69 @@ void BulkMoveArtifacts::applyGs(CGameState * gs)
|
|||||||
void AssembledArtifact::applyGs(CGameState *gs)
|
void AssembledArtifact::applyGs(CGameState *gs)
|
||||||
{
|
{
|
||||||
CArtifactSet * artSet = al.getHolderArtSet();
|
CArtifactSet * artSet = al.getHolderArtSet();
|
||||||
[[maybe_unused]] const CArtifactInstance *transformedArt = al.getArt();
|
const CArtifactInstance * transformedArt = al.getArt();
|
||||||
assert(transformedArt);
|
assert(transformedArt);
|
||||||
bool combineEquipped = !ArtifactUtils::isSlotBackpack(al.slot);
|
assert(vstd::contains_if(ArtifactUtils::assemblyPossibilities(artSet, transformedArt->getTypeId()), [=](const CArtifact * art)->bool
|
||||||
assert(vstd::contains_if(ArtifactUtils::assemblyPossibilities(artSet, transformedArt->artType->getId(), combineEquipped), [=](const CArtifact * art)->bool
|
|
||||||
{
|
{
|
||||||
return art->getId() == builtArt->getId();
|
return art->getId() == builtArt->getId();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const auto transformedArtSlot = artSet->getSlotByInstance(transformedArt);
|
||||||
auto * combinedArt = new CArtifactInstance(builtArt);
|
auto * combinedArt = new CArtifactInstance(builtArt);
|
||||||
gs->map->addNewArtifactInstance(combinedArt);
|
gs->map->addNewArtifactInstance(combinedArt);
|
||||||
// Retrieve all constituents
|
|
||||||
for(const CArtifact * constituent : builtArt->getConstituents())
|
|
||||||
{
|
|
||||||
ArtifactPosition pos = combineEquipped ? artSet->getArtPos(constituent->getId(), true, false) :
|
|
||||||
artSet->getArtBackpackPos(constituent->getId());
|
|
||||||
assert(pos != ArtifactPosition::PRE_FIRST);
|
|
||||||
CArtifactInstance * constituentInstance = artSet->getArt(pos);
|
|
||||||
|
|
||||||
//move constituent from hero to be part of new, combined artifact
|
// Find slots for all involved artifacts
|
||||||
constituentInstance->removeFrom(ArtifactLocation(al.artHolder, pos));
|
std::vector<ArtifactPosition> slotsInvolved;
|
||||||
if(combineEquipped)
|
for(const auto constituent : builtArt->getConstituents())
|
||||||
|
{
|
||||||
|
ArtifactPosition slot;
|
||||||
|
if(transformedArt->getTypeId() == constituent->getId())
|
||||||
|
slot = transformedArtSlot;
|
||||||
|
else
|
||||||
|
slot = artSet->getArtPos(constituent->getId(), false, false);
|
||||||
|
|
||||||
|
assert(slot != ArtifactPosition::PRE_FIRST);
|
||||||
|
slotsInvolved.emplace_back(slot);
|
||||||
|
}
|
||||||
|
std::sort(slotsInvolved.begin(), slotsInvolved.end(), std::greater<>());
|
||||||
|
|
||||||
|
// Find a slot for combined artifact
|
||||||
|
al.slot = transformedArtSlot;
|
||||||
|
for(const auto slot : slotsInvolved)
|
||||||
|
{
|
||||||
|
if(ArtifactUtils::isSlotEquipment(transformedArtSlot))
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if(ArtifactUtils::isSlotBackpack(slot))
|
||||||
|
{
|
||||||
|
al.slot = ArtifactPosition::BACKPACK_START;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if(!vstd::contains(combinedArt->artType->getPossibleSlots().at(artSet->bearerType()), al.slot)
|
if(!vstd::contains(combinedArt->artType->getPossibleSlots().at(artSet->bearerType()), al.slot)
|
||||||
&& vstd::contains(combinedArt->artType->getPossibleSlots().at(artSet->bearerType()), pos))
|
&& vstd::contains(combinedArt->artType->getPossibleSlots().at(artSet->bearerType()), slot))
|
||||||
al.slot = pos;
|
al.slot = slot;
|
||||||
if(al.slot == pos)
|
|
||||||
pos = ArtifactPosition::PRE_FIRST;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
al.slot = std::min(al.slot, pos);
|
if(ArtifactUtils::isSlotBackpack(slot))
|
||||||
pos = ArtifactPosition::PRE_FIRST;
|
al.slot = std::min(al.slot, slot);
|
||||||
}
|
}
|
||||||
combinedArt->addPart(constituentInstance, pos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//put new combined artifacts
|
// Delete parts from hero
|
||||||
|
for(const auto slot : slotsInvolved)
|
||||||
|
{
|
||||||
|
const auto constituentInstance = artSet->getArt(slot);
|
||||||
|
constituentInstance->removeFrom(ArtifactLocation(al.artHolder, slot));
|
||||||
|
|
||||||
|
if(ArtifactUtils::isSlotEquipment(al.slot) && slot != al.slot)
|
||||||
|
combinedArt->addPart(constituentInstance, slot);
|
||||||
|
else
|
||||||
|
combinedArt->addPart(constituentInstance, ArtifactPosition::PRE_FIRST);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put new combined artifacts
|
||||||
combinedArt->putAt(al);
|
combinedArt->putAt(al);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,8 +62,8 @@ PlayerSettings * StartInfo::getPlayersSettings(const ui8 connectedPlayerId)
|
|||||||
|
|
||||||
std::string StartInfo::getCampaignName() const
|
std::string StartInfo::getCampaignName() const
|
||||||
{
|
{
|
||||||
if(!campState->getName().empty())
|
if(!campState->getNameTranslated().empty())
|
||||||
return campState->getName();
|
return campState->getNameTranslated();
|
||||||
else
|
else
|
||||||
return VLC->generaltexth->allTexts[508];
|
return VLC->generaltexth->allTexts[508];
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ struct DLL_LINKAGE PlayerSettings
|
|||||||
HeroTypeID hero;
|
HeroTypeID hero;
|
||||||
HeroTypeID heroPortrait; //-1 if default, else ID
|
HeroTypeID heroPortrait; //-1 if default, else ID
|
||||||
|
|
||||||
std::string heroName;
|
std::string heroNameTextId;
|
||||||
PlayerColor color; //from 0 -
|
PlayerColor color; //from 0 -
|
||||||
enum EHandicap {NO_HANDICAP, MILD, SEVERE};
|
enum EHandicap {NO_HANDICAP, MILD, SEVERE};
|
||||||
EHandicap handicap;//0-no, 1-mild, 2-severe
|
EHandicap handicap;//0-no, 1-mild, 2-severe
|
||||||
@ -73,7 +73,7 @@ struct DLL_LINKAGE PlayerSettings
|
|||||||
h & castle;
|
h & castle;
|
||||||
h & hero;
|
h & hero;
|
||||||
h & heroPortrait;
|
h & heroPortrait;
|
||||||
h & heroName;
|
h & heroNameTextId;
|
||||||
h & bonus;
|
h & bonus;
|
||||||
h & color;
|
h & color;
|
||||||
h & handicap;
|
h & handicap;
|
||||||
|
@ -134,7 +134,7 @@ std::string CampaignHandler::readLocalizedString(CBinaryReader & reader, std::st
|
|||||||
return "";
|
return "";
|
||||||
|
|
||||||
VLC->generaltexth->registerString(modName, stringID, input);
|
VLC->generaltexth->registerString(modName, stringID, input);
|
||||||
return VLC->generaltexth->translate(stringID.get());
|
return stringID.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CampaignHandler::readHeaderFromJson(CampaignHeader & ret, JsonNode & reader, std::string filename, std::string modName, std::string encoding)
|
void CampaignHandler::readHeaderFromJson(CampaignHeader & ret, JsonNode & reader, std::string filename, std::string modName, std::string encoding)
|
||||||
@ -149,8 +149,8 @@ void CampaignHandler::readHeaderFromJson(CampaignHeader & ret, JsonNode & reader
|
|||||||
ret.version = CampaignVersion::VCMI;
|
ret.version = CampaignVersion::VCMI;
|
||||||
ret.campaignRegions = CampaignRegions::fromJson(reader["regions"]);
|
ret.campaignRegions = CampaignRegions::fromJson(reader["regions"]);
|
||||||
ret.numberOfScenarios = reader["scenarios"].Vector().size();
|
ret.numberOfScenarios = reader["scenarios"].Vector().size();
|
||||||
ret.name = reader["name"].String();
|
ret.name.appendTextID(reader["name"].String());
|
||||||
ret.description = reader["description"].String();
|
ret.description.appendTextID(reader["description"].String());
|
||||||
ret.difficultyChoosenByPlayer = reader["allowDifficultySelection"].Bool();
|
ret.difficultyChoosenByPlayer = reader["allowDifficultySelection"].Bool();
|
||||||
ret.music = AudioPath::fromJson(reader["music"]);
|
ret.music = AudioPath::fromJson(reader["music"]);
|
||||||
ret.filename = filename;
|
ret.filename = filename;
|
||||||
@ -169,7 +169,7 @@ CampaignScenario CampaignHandler::readScenarioFromJson(JsonNode & reader)
|
|||||||
ret.prologVideo = VideoPath::fromJson(identifier["video"]);
|
ret.prologVideo = VideoPath::fromJson(identifier["video"]);
|
||||||
ret.prologMusic = AudioPath::fromJson(identifier["music"]);
|
ret.prologMusic = AudioPath::fromJson(identifier["music"]);
|
||||||
ret.prologVoice = AudioPath::fromJson(identifier["voice"]);
|
ret.prologVoice = AudioPath::fromJson(identifier["voice"]);
|
||||||
ret.prologText = identifier["text"].String();
|
ret.prologText.jsonDeserialize(identifier["text"]);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
@ -181,7 +181,7 @@ CampaignScenario CampaignHandler::readScenarioFromJson(JsonNode & reader)
|
|||||||
|
|
||||||
ret.regionColor = reader["color"].Integer();
|
ret.regionColor = reader["color"].Integer();
|
||||||
ret.difficulty = reader["difficulty"].Integer();
|
ret.difficulty = reader["difficulty"].Integer();
|
||||||
ret.regionText = reader["regionText"].String();
|
ret.regionText.jsonDeserialize(reader["regionText"]);
|
||||||
ret.prolog = prologEpilogReader(reader["prolog"]);
|
ret.prolog = prologEpilogReader(reader["prolog"]);
|
||||||
ret.epilog = prologEpilogReader(reader["epilog"]);
|
ret.epilog = prologEpilogReader(reader["epilog"]);
|
||||||
|
|
||||||
@ -383,8 +383,8 @@ void CampaignHandler::readHeaderFromMemory( CampaignHeader & ret, CBinaryReader
|
|||||||
ret.version = static_cast<CampaignVersion>(reader.readUInt32());
|
ret.version = static_cast<CampaignVersion>(reader.readUInt32());
|
||||||
ui8 campId = reader.readUInt8() - 1;//change range of it from [1, 20] to [0, 19]
|
ui8 campId = reader.readUInt8() - 1;//change range of it from [1, 20] to [0, 19]
|
||||||
ret.loadLegacyData(campId);
|
ret.loadLegacyData(campId);
|
||||||
ret.name = readLocalizedString(reader, filename, modName, encoding, "name");
|
ret.name.appendTextID(readLocalizedString(reader, filename, modName, encoding, "name"));
|
||||||
ret.description = readLocalizedString(reader, filename, modName, encoding, "description");
|
ret.description.appendTextID(readLocalizedString(reader, filename, modName, encoding, "description"));
|
||||||
if (ret.version > CampaignVersion::RoE)
|
if (ret.version > CampaignVersion::RoE)
|
||||||
ret.difficultyChoosenByPlayer = reader.readInt8();
|
ret.difficultyChoosenByPlayer = reader.readInt8();
|
||||||
else
|
else
|
||||||
@ -410,7 +410,7 @@ CampaignScenario CampaignHandler::readScenarioFromMemory( CBinaryReader & reader
|
|||||||
ret.prologVideo = CampaignHandler::prologVideoName(index);
|
ret.prologVideo = CampaignHandler::prologVideoName(index);
|
||||||
ret.prologMusic = CampaignHandler::prologMusicName(reader.readUInt8());
|
ret.prologMusic = CampaignHandler::prologMusicName(reader.readUInt8());
|
||||||
ret.prologVoice = isOriginalCampaign ? CampaignHandler::prologVoiceName(index) : AudioPath();
|
ret.prologVoice = isOriginalCampaign ? CampaignHandler::prologVoiceName(index) : AudioPath();
|
||||||
ret.prologText = readLocalizedString(reader, header.filename, header.modName, header.encoding, identifier);
|
ret.prologText.appendTextID(readLocalizedString(reader, header.filename, header.modName, header.encoding, identifier));
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
@ -428,7 +428,7 @@ CampaignScenario CampaignHandler::readScenarioFromMemory( CBinaryReader & reader
|
|||||||
}
|
}
|
||||||
ret.regionColor = reader.readUInt8();
|
ret.regionColor = reader.readUInt8();
|
||||||
ret.difficulty = reader.readUInt8();
|
ret.difficulty = reader.readUInt8();
|
||||||
ret.regionText = readLocalizedString(reader, header.filename, header.modName, header.encoding, ret.mapName + ".region");
|
ret.regionText.appendTextID(readLocalizedString(reader, header.filename, header.modName, header.encoding, ret.mapName + ".region"));
|
||||||
ret.prolog = prologEpilogReader(ret.mapName + ".prolog");
|
ret.prolog = prologEpilogReader(ret.mapName + ".prolog");
|
||||||
ret.epilog = prologEpilogReader(ret.mapName + ".epilog");
|
ret.epilog = prologEpilogReader(ret.mapName + ".epilog");
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../filesystem/ResourcePath.h"
|
#include "../filesystem/ResourcePath.h"
|
||||||
|
#include "../MetaString.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
@ -19,7 +20,7 @@ struct DLL_LINKAGE CampaignScenarioPrologEpilog
|
|||||||
VideoPath prologVideo;
|
VideoPath prologVideo;
|
||||||
AudioPath prologMusic; // from CmpMusic.txt
|
AudioPath prologMusic; // from CmpMusic.txt
|
||||||
AudioPath prologVoice;
|
AudioPath prologVoice;
|
||||||
std::string prologText;
|
MetaString prologText;
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int formatVersion)
|
template <typename Handler> void serialize(Handler &h, const int formatVersion)
|
||||||
{
|
{
|
||||||
|
@ -134,14 +134,14 @@ bool CampaignHeader::formatVCMI() const
|
|||||||
return version == CampaignVersion::VCMI;
|
return version == CampaignVersion::VCMI;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CampaignHeader::getDescription() const
|
std::string CampaignHeader::getDescriptionTranslated() const
|
||||||
{
|
{
|
||||||
return description;
|
return description.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CampaignHeader::getName() const
|
std::string CampaignHeader::getNameTranslated() const
|
||||||
{
|
{
|
||||||
return name;
|
return name.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CampaignHeader::getFilename() const
|
std::string CampaignHeader::getFilename() const
|
||||||
@ -267,7 +267,7 @@ void CampaignState::setCurrentMapAsConquered(std::vector<CGHeroInstance *> heroe
|
|||||||
return a->getHeroStrength() > b->getHeroStrength();
|
return a->getHeroStrength() > b->getHeroStrength();
|
||||||
});
|
});
|
||||||
|
|
||||||
logGlobal->info("Scenario %d of campaign %s (%s) has been completed", static_cast<int>(*currentMap), getFilename(), getName());
|
logGlobal->info("Scenario %d of campaign %s (%s) has been completed", static_cast<int>(*currentMap), getFilename(), getNameTranslated());
|
||||||
|
|
||||||
mapsConquered.push_back(*currentMap);
|
mapsConquered.push_back(*currentMap);
|
||||||
auto reservedHeroes = getReservedHeroes();
|
auto reservedHeroes = getReservedHeroes();
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../lib/GameConstants.h"
|
#include "../lib/GameConstants.h"
|
||||||
|
#include "../lib/MetaString.h"
|
||||||
#include "../lib/filesystem/ResourcePath.h"
|
#include "../lib/filesystem/ResourcePath.h"
|
||||||
#include "CampaignConstants.h"
|
#include "CampaignConstants.h"
|
||||||
#include "CampaignScenarioPrologEpilog.h"
|
#include "CampaignScenarioPrologEpilog.h"
|
||||||
@ -74,8 +75,8 @@ class DLL_LINKAGE CampaignHeader : public boost::noncopyable
|
|||||||
|
|
||||||
CampaignVersion version = CampaignVersion::NONE;
|
CampaignVersion version = CampaignVersion::NONE;
|
||||||
CampaignRegions campaignRegions;
|
CampaignRegions campaignRegions;
|
||||||
std::string name;
|
MetaString name;
|
||||||
std::string description;
|
MetaString description;
|
||||||
AudioPath music;
|
AudioPath music;
|
||||||
std::string filename;
|
std::string filename;
|
||||||
std::string modName;
|
std::string modName;
|
||||||
@ -90,8 +91,8 @@ public:
|
|||||||
bool playerSelectedDifficulty() const;
|
bool playerSelectedDifficulty() const;
|
||||||
bool formatVCMI() const;
|
bool formatVCMI() const;
|
||||||
|
|
||||||
std::string getDescription() const;
|
std::string getDescriptionTranslated() const;
|
||||||
std::string getName() const;
|
std::string getNameTranslated() const;
|
||||||
std::string getFilename() const;
|
std::string getFilename() const;
|
||||||
std::string getModName() const;
|
std::string getModName() const;
|
||||||
std::string getEncoding() const;
|
std::string getEncoding() const;
|
||||||
@ -176,12 +177,12 @@ struct DLL_LINKAGE CampaignTravel
|
|||||||
struct DLL_LINKAGE CampaignScenario
|
struct DLL_LINKAGE CampaignScenario
|
||||||
{
|
{
|
||||||
std::string mapName; //*.h3m
|
std::string mapName; //*.h3m
|
||||||
std::string scenarioName; //from header. human-readble
|
MetaString scenarioName; //from header
|
||||||
std::set<CampaignScenarioID> preconditionRegions; //what we need to conquer to conquer this one (stored as bitfield in h3c)
|
std::set<CampaignScenarioID> preconditionRegions; //what we need to conquer to conquer this one (stored as bitfield in h3c)
|
||||||
ui8 regionColor = 0;
|
ui8 regionColor = 0;
|
||||||
ui8 difficulty = 0;
|
ui8 difficulty = 0;
|
||||||
|
|
||||||
std::string regionText;
|
MetaString regionText;
|
||||||
CampaignScenarioPrologEpilog prolog;
|
CampaignScenarioPrologEpilog prolog;
|
||||||
CampaignScenarioPrologEpilog epilog;
|
CampaignScenarioPrologEpilog epilog;
|
||||||
|
|
||||||
|
@ -1020,7 +1020,7 @@ void CGameState::initTowns()
|
|||||||
if(vti->getNameTranslated().empty())
|
if(vti->getNameTranslated().empty())
|
||||||
{
|
{
|
||||||
size_t nameID = getRandomGenerator().nextInt(vti->getTown()->getRandomNamesCount() - 1);
|
size_t nameID = getRandomGenerator().nextInt(vti->getTown()->getRandomNamesCount() - 1);
|
||||||
vti->setNameTranslated(vti->getTown()->getRandomNameTranslated(nameID));
|
vti->setNameTextId(vti->getTown()->getRandomNameTextID(nameID));
|
||||||
}
|
}
|
||||||
|
|
||||||
static const BuildingID basicDwellings[] = { BuildingID::DWELL_FIRST, BuildingID::DWELL_LVL_2, BuildingID::DWELL_LVL_3, BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7 };
|
static const BuildingID basicDwellings[] = { BuildingID::DWELL_FIRST, BuildingID::DWELL_LVL_2, BuildingID::DWELL_LVL_3, BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7 };
|
||||||
|
@ -108,7 +108,7 @@ void CGCreature::onHeroVisit( const CGHeroInstance * h ) const
|
|||||||
{
|
{
|
||||||
InfoWindow iw;
|
InfoWindow iw;
|
||||||
iw.player = h->tempOwner;
|
iw.player = h->tempOwner;
|
||||||
iw.text.appendRawString(message);
|
iw.text = message;
|
||||||
iw.type = EInfoWindowMode::MODAL;
|
iw.type = EInfoWindowMode::MODAL;
|
||||||
cb->showInfoDialog(&iw);
|
cb->showInfoDialog(&iw);
|
||||||
}
|
}
|
||||||
@ -578,7 +578,7 @@ void CGCreature::serializeJsonOptions(JsonSerializeFormat & handler)
|
|||||||
|
|
||||||
handler.serializeBool("noGrowing", notGrowingTeam);
|
handler.serializeBool("noGrowing", notGrowingTeam);
|
||||||
handler.serializeBool("neverFlees", neverFlees);
|
handler.serializeBool("neverFlees", neverFlees);
|
||||||
handler.serializeString("rewardMessage", message);
|
handler.serializeStruct("rewardMessage", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include "CArmedInstance.h"
|
#include "CArmedInstance.h"
|
||||||
#include "../ResourceSet.h"
|
#include "../ResourceSet.h"
|
||||||
|
#include "../MetaString.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
@ -27,7 +28,7 @@ public:
|
|||||||
|
|
||||||
ui32 identifier; //unique code for this monster (used in missions)
|
ui32 identifier; //unique code for this monster (used in missions)
|
||||||
si8 character; //character of this set of creatures (0 - the most friendly, 4 - the most hostile) => on init changed to -4 (compliant) ... 10 value (savage)
|
si8 character; //character of this set of creatures (0 - the most friendly, 4 - the most hostile) => on init changed to -4 (compliant) ... 10 value (savage)
|
||||||
std::string message; //message printed for attacking hero
|
MetaString message; //message printed for attacking hero
|
||||||
TResources resources; // resources given to hero that has won with monsters
|
TResources resources; // resources given to hero that has won with monsters
|
||||||
ArtifactID gainedArtifact; //ID of artifact gained to hero, -1 if none
|
ArtifactID gainedArtifact; //ID of artifact gained to hero, -1 if none
|
||||||
bool neverFlees; //if true, the troops will never flee
|
bool neverFlees; //if true, the troops will never flee
|
||||||
|
@ -1030,15 +1030,13 @@ si32 CGHeroInstance::manaLimit() const
|
|||||||
|
|
||||||
std::string CGHeroInstance::getNameTranslated() const
|
std::string CGHeroInstance::getNameTranslated() const
|
||||||
{
|
{
|
||||||
if (!nameCustom.empty())
|
|
||||||
return nameCustom;
|
|
||||||
return VLC->generaltexth->translate(getNameTextID());
|
return VLC->generaltexth->translate(getNameTextID());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CGHeroInstance::getNameTextID() const
|
std::string CGHeroInstance::getNameTextID() const
|
||||||
{
|
{
|
||||||
if (!nameCustom.empty())
|
if (!nameCustomTextId.empty())
|
||||||
return nameCustom;
|
return nameCustomTextId;
|
||||||
if (type)
|
if (type)
|
||||||
return type->getNameTextID();
|
return type->getNameTextID();
|
||||||
|
|
||||||
@ -1049,16 +1047,13 @@ std::string CGHeroInstance::getNameTextID() const
|
|||||||
|
|
||||||
std::string CGHeroInstance::getBiographyTranslated() const
|
std::string CGHeroInstance::getBiographyTranslated() const
|
||||||
{
|
{
|
||||||
if (!biographyCustom.empty())
|
|
||||||
return biographyCustom;
|
|
||||||
|
|
||||||
return VLC->generaltexth->translate(getBiographyTextID());
|
return VLC->generaltexth->translate(getBiographyTextID());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CGHeroInstance::getBiographyTextID() const
|
std::string CGHeroInstance::getBiographyTextID() const
|
||||||
{
|
{
|
||||||
if (!biographyCustom.empty())
|
if (!biographyCustomTextId.empty())
|
||||||
return biographyCustom;
|
return biographyCustomTextId;
|
||||||
if (type)
|
if (type)
|
||||||
return type->getBiographyTextID();
|
return type->getBiographyTextID();
|
||||||
|
|
||||||
@ -1520,7 +1515,7 @@ void CGHeroInstance::updateFrom(const JsonNode & data)
|
|||||||
|
|
||||||
void CGHeroInstance::serializeCommonOptions(JsonSerializeFormat & handler)
|
void CGHeroInstance::serializeCommonOptions(JsonSerializeFormat & handler)
|
||||||
{
|
{
|
||||||
handler.serializeString("biography", biographyCustom);
|
handler.serializeString("biography", biographyCustomTextId);
|
||||||
handler.serializeInt("experience", exp, 0);
|
handler.serializeInt("experience", exp, 0);
|
||||||
|
|
||||||
if(!handler.saving && exp != UNINITIALIZED_EXPERIENCE) //do not gain levels if experience is not initialized
|
if(!handler.saving && exp != UNINITIALIZED_EXPERIENCE) //do not gain levels if experience is not initialized
|
||||||
@ -1531,7 +1526,7 @@ void CGHeroInstance::serializeCommonOptions(JsonSerializeFormat & handler)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handler.serializeString("name", nameCustom);
|
handler.serializeString("name", nameCustomTextId);
|
||||||
handler.serializeInt("gender", gender, 0);
|
handler.serializeInt("gender", gender, 0);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -74,8 +74,8 @@ public:
|
|||||||
std::vector<std::pair<SecondarySkill,ui8> > secSkills; //first - ID of skill, second - level of skill (1 - basic, 2 - adv., 3 - expert); if hero has ability (-1, -1) it meansthat it should have default secondary abilities
|
std::vector<std::pair<SecondarySkill,ui8> > secSkills; //first - ID of skill, second - level of skill (1 - basic, 2 - adv., 3 - expert); if hero has ability (-1, -1) it meansthat it should have default secondary abilities
|
||||||
EHeroGender gender;
|
EHeroGender gender;
|
||||||
|
|
||||||
std::string nameCustom;
|
std::string nameCustomTextId;
|
||||||
std::string biographyCustom;
|
std::string biographyCustomTextId;
|
||||||
|
|
||||||
bool inTownGarrison; // if hero is in town garrison
|
bool inTownGarrison; // if hero is in town garrison
|
||||||
ConstTransitivePtr<CGTownInstance> visitedTown; //set if hero is visiting town or in the town garrison
|
ConstTransitivePtr<CGTownInstance> visitedTown; //set if hero is visiting town or in the town garrison
|
||||||
@ -319,8 +319,8 @@ public:
|
|||||||
h & static_cast<CArtifactSet&>(*this);
|
h & static_cast<CArtifactSet&>(*this);
|
||||||
h & exp;
|
h & exp;
|
||||||
h & level;
|
h & level;
|
||||||
h & nameCustom;
|
h & nameCustomTextId;
|
||||||
h & biographyCustom;
|
h & biographyCustomTextId;
|
||||||
h & portrait;
|
h & portrait;
|
||||||
h & mana;
|
h & mana;
|
||||||
h & secSkills;
|
h & secSkills;
|
||||||
|
@ -35,7 +35,7 @@ void CGPandoraBox::init()
|
|||||||
{
|
{
|
||||||
i.reward.removeObject = true;
|
i.reward.removeObject = true;
|
||||||
if(!message.empty() && i.message.empty())
|
if(!message.empty() && i.message.empty())
|
||||||
i.message = MetaString::createFromRawString(message);
|
i.message = message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +101,7 @@ void CGPandoraBox::grantRewardWithMessage(const CGHeroInstance * h, int index, b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(vi.reward.manaDiff || vi.reward.manaPercentage)
|
if(vi.reward.manaDiff || vi.reward.manaPercentage >= 0)
|
||||||
txt = setText(temp.manaDiff > 0, 177, 176, h);
|
txt = setText(temp.manaDiff > 0, 177, 176, h);
|
||||||
|
|
||||||
for(auto b : vi.reward.bonuses)
|
for(auto b : vi.reward.bonuses)
|
||||||
@ -155,7 +155,7 @@ void CGPandoraBox::grantRewardWithMessage(const CGHeroInstance * h, int index, b
|
|||||||
temp.resources.amin(0);
|
temp.resources.amin(0);
|
||||||
temp.resources.amax(0);
|
temp.resources.amax(0);
|
||||||
temp.manaDiff = 0;
|
temp.manaDiff = 0;
|
||||||
temp.manaPercentage = 0;
|
temp.manaPercentage = -1;
|
||||||
temp.spells.clear();
|
temp.spells.clear();
|
||||||
temp.creatures.clear();
|
temp.creatures.clear();
|
||||||
temp.bonuses.clear();
|
temp.bonuses.clear();
|
||||||
@ -209,7 +209,7 @@ void CGPandoraBox::serializeJsonOptions(JsonSerializeFormat & handler)
|
|||||||
{
|
{
|
||||||
CRewardableObject::serializeJsonOptions(handler);
|
CRewardableObject::serializeJsonOptions(handler);
|
||||||
|
|
||||||
handler.serializeString("guardMessage", message);
|
handler.serializeStruct("guardMessage", message);
|
||||||
|
|
||||||
if(!handler.saving)
|
if(!handler.saving)
|
||||||
{
|
{
|
||||||
@ -297,7 +297,7 @@ void CGEvent::init()
|
|||||||
{
|
{
|
||||||
i.reward.removeObject = removeAfterVisit;
|
i.reward.removeObject = removeAfterVisit;
|
||||||
if(!message.empty() && i.message.empty())
|
if(!message.empty() && i.message.empty())
|
||||||
i.message = MetaString::createFromRawString(message);
|
i.message = message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,7 +327,7 @@ void CGEvent::activated( const CGHeroInstance * h ) const
|
|||||||
InfoWindow iw;
|
InfoWindow iw;
|
||||||
iw.player = h->tempOwner;
|
iw.player = h->tempOwner;
|
||||||
if(!message.empty())
|
if(!message.empty())
|
||||||
iw.text.appendRawString(message);
|
iw.text = message;
|
||||||
else
|
else
|
||||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 16);
|
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 16);
|
||||||
cb->showInfoDialog(&iw);
|
cb->showInfoDialog(&iw);
|
||||||
|
@ -19,7 +19,7 @@ struct InfoWindow;
|
|||||||
class DLL_LINKAGE CGPandoraBox : public CRewardableObject
|
class DLL_LINKAGE CGPandoraBox : public CRewardableObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::string message;
|
MetaString message;
|
||||||
|
|
||||||
void initObj(CRandomGenerator & rand) override;
|
void initObj(CRandomGenerator & rand) override;
|
||||||
void onHeroVisit(const CGHeroInstance * h) const override;
|
void onHeroVisit(const CGHeroInstance * h) const override;
|
||||||
|
@ -327,7 +327,7 @@ void CGTownInstance::onHeroVisit(const CGHeroInstance * h) const
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logGlobal->error("%s visits allied town of %s from different pos?", h->getNameTranslated(), name);
|
logGlobal->error("%s visits allied town of %s from different pos?", h->getNameTranslated(), getNameTranslated());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,15 +337,15 @@ void CGTownInstance::onHeroLeave(const CGHeroInstance * h) const
|
|||||||
if(visitingHero == h)
|
if(visitingHero == h)
|
||||||
{
|
{
|
||||||
cb->stopHeroVisitCastle(this, h);
|
cb->stopHeroVisitCastle(this, h);
|
||||||
logGlobal->trace("%s correctly left town %s", h->getNameTranslated(), name);
|
logGlobal->trace("%s correctly left town %s", h->getNameTranslated(), getNameTranslated());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
logGlobal->warn("Warning, %s tries to leave the town %s but hero is not inside.", h->getNameTranslated(), name);
|
logGlobal->warn("Warning, %s tries to leave the town %s but hero is not inside.", h->getNameTranslated(), getNameTranslated());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CGTownInstance::getObjectName() const
|
std::string CGTownInstance::getObjectName() const
|
||||||
{
|
{
|
||||||
return name + ", " + town->faction->getNameTranslated();
|
return getNameTranslated() + ", " + town->faction->getNameTranslated();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CGTownInstance::townEnvisagesBuilding(BuildingSubID::EBuildingSubID subId) const
|
bool CGTownInstance::townEnvisagesBuilding(BuildingSubID::EBuildingSubID subId) const
|
||||||
@ -767,7 +767,7 @@ void CGTownInstance::updateAppearance()
|
|||||||
|
|
||||||
std::string CGTownInstance::nodeName() const
|
std::string CGTownInstance::nodeName() const
|
||||||
{
|
{
|
||||||
return "Town (" + (town ? town->faction->getNameTranslated() : "unknown") + ") of " + name;
|
return "Town (" + (town ? town->faction->getNameTranslated() : "unknown") + ") of " + getNameTranslated();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGTownInstance::deserializationFix()
|
void CGTownInstance::deserializationFix()
|
||||||
@ -915,12 +915,12 @@ CBonusSystemNode & CGTownInstance::whatShouldBeAttached()
|
|||||||
|
|
||||||
std::string CGTownInstance::getNameTranslated() const
|
std::string CGTownInstance::getNameTranslated() const
|
||||||
{
|
{
|
||||||
return name;
|
return VLC->generaltexth->translate(nameTextId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGTownInstance::setNameTranslated( const std::string & newName )
|
void CGTownInstance::setNameTextId( const std::string & newName )
|
||||||
{
|
{
|
||||||
name = newName;
|
nameTextId = newName;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CArmedInstance * CGTownInstance::getUpperArmy() const
|
const CArmedInstance * CGTownInstance::getUpperArmy() const
|
||||||
@ -980,7 +980,7 @@ TResources CGTownInstance::getBuildingCost(const BuildingID & buildingID) const
|
|||||||
return town->buildings.at(buildingID)->resources;
|
return town->buildings.at(buildingID)->resources;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logGlobal->error("Town %s at %s has no possible building %d!", name, pos.toString(), buildingID.toEnum());
|
logGlobal->error("Town %s at %s has no possible building %d!", getNameTranslated(), pos.toString(), buildingID.toEnum());
|
||||||
return TResources();
|
return TResources();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1097,7 +1097,7 @@ void CGTownInstance::serializeJsonOptions(JsonSerializeFormat & handler)
|
|||||||
if(!handler.saving)
|
if(!handler.saving)
|
||||||
handler.serializeEnum("tightFormation", formation, NArmyFormation::names); //for old format
|
handler.serializeEnum("tightFormation", formation, NArmyFormation::names); //for old format
|
||||||
CArmedInstance::serializeJsonOptions(handler);
|
CArmedInstance::serializeJsonOptions(handler);
|
||||||
handler.serializeString("name", name);
|
handler.serializeString("name", nameTextId);
|
||||||
|
|
||||||
{
|
{
|
||||||
auto decodeBuilding = [this](const std::string & identifier) -> si32
|
auto decodeBuilding = [this](const std::string & identifier) -> si32
|
||||||
|
@ -44,7 +44,7 @@ struct DLL_LINKAGE GrowthInfo
|
|||||||
|
|
||||||
class DLL_LINKAGE CGTownInstance : public CGDwelling, public IShipyard, public IMarket, public INativeTerrainProvider, public ICreatureUpgrader
|
class DLL_LINKAGE CGTownInstance : public CGDwelling, public IShipyard, public IMarket, public INativeTerrainProvider, public ICreatureUpgrader
|
||||||
{
|
{
|
||||||
std::string name; // name of town
|
std::string nameTextId; // name of town
|
||||||
public:
|
public:
|
||||||
using CGDwelling::getPosition;
|
using CGDwelling::getPosition;
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ public:
|
|||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
{
|
{
|
||||||
h & static_cast<CGDwelling&>(*this);
|
h & static_cast<CGDwelling&>(*this);
|
||||||
h & name;
|
h & nameTextId;
|
||||||
h & builded;
|
h & builded;
|
||||||
h & destroyed;
|
h & destroyed;
|
||||||
h & identifier;
|
h & identifier;
|
||||||
@ -102,7 +102,7 @@ public:
|
|||||||
{
|
{
|
||||||
if(!town->buildings.count(building) || !town->buildings.at(building))
|
if(!town->buildings.count(building) || !town->buildings.at(building))
|
||||||
{
|
{
|
||||||
logGlobal->error("#1444-like issue in CGTownInstance::serialize. From town %s at %s removing the bogus builtBuildings item %s", name, pos.toString(), building);
|
logGlobal->error("#1444-like issue in CGTownInstance::serialize. From town %s at %s removing the bogus builtBuildings item %s", nameTextId, pos.toString(), building);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -126,7 +126,7 @@ public:
|
|||||||
const CArmedInstance *getUpperArmy() const; //garrisoned hero if present or the town itself
|
const CArmedInstance *getUpperArmy() const; //garrisoned hero if present or the town itself
|
||||||
|
|
||||||
std::string getNameTranslated() const;
|
std::string getNameTranslated() const;
|
||||||
void setNameTranslated(const std::string & newName);
|
void setNameTextId(const std::string & newName);
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@ -181,20 +181,20 @@ bool CQuest::checkQuest(const CGHeroInstance * h) const
|
|||||||
|
|
||||||
void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &components, bool isCustom, bool firstVisit, const CGHeroInstance * h) const
|
void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &components, bool isCustom, bool firstVisit, const CGHeroInstance * h) const
|
||||||
{
|
{
|
||||||
std::string text;
|
MetaString text;
|
||||||
bool failRequirements = (h ? !checkQuest(h) : true);
|
bool failRequirements = (h ? !checkQuest(h) : true);
|
||||||
|
|
||||||
if(firstVisit)
|
if(firstVisit)
|
||||||
{
|
{
|
||||||
isCustom = isCustomFirst;
|
isCustom = isCustomFirst;
|
||||||
text = firstVisitText;
|
text = firstVisitText;
|
||||||
iwText.appendRawString(text);
|
iwText.appendRawString(text.toString());
|
||||||
}
|
}
|
||||||
else if(failRequirements)
|
else if(failRequirements)
|
||||||
{
|
{
|
||||||
isCustom = isCustomNext;
|
isCustom = isCustomNext;
|
||||||
text = nextVisitText;
|
text = nextVisitText;
|
||||||
iwText.appendRawString(text);
|
iwText.appendRawString(text.toString());
|
||||||
}
|
}
|
||||||
switch (missionType)
|
switch (missionType)
|
||||||
{
|
{
|
||||||
@ -223,7 +223,7 @@ void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &components
|
|||||||
case MISSION_KILL_HERO:
|
case MISSION_KILL_HERO:
|
||||||
components.emplace_back(Component::EComponentType::HERO_PORTRAIT, heroPortrait, 0, 0);
|
components.emplace_back(Component::EComponentType::HERO_PORTRAIT, heroPortrait, 0, 0);
|
||||||
if(!isCustom)
|
if(!isCustom)
|
||||||
addReplacements(iwText, text);
|
addReplacements(iwText, text.toString());
|
||||||
break;
|
break;
|
||||||
case MISSION_HERO:
|
case MISSION_HERO:
|
||||||
//FIXME: portrait may not match hero, if custom portrait was set in map editor
|
//FIXME: portrait may not match hero, if custom portrait was set in map editor
|
||||||
@ -236,7 +236,7 @@ void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &components
|
|||||||
components.emplace_back(stackToKill);
|
components.emplace_back(stackToKill);
|
||||||
if(!isCustom)
|
if(!isCustom)
|
||||||
{
|
{
|
||||||
addReplacements(iwText, text);
|
addReplacements(iwText, text.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -286,7 +286,7 @@ void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &components
|
|||||||
case MISSION_PLAYER:
|
case MISSION_PLAYER:
|
||||||
components.emplace_back(Component::EComponentType::FLAG, m13489val, 0, 0);
|
components.emplace_back(Component::EComponentType::FLAG, m13489val, 0, 0);
|
||||||
if(!isCustom)
|
if(!isCustom)
|
||||||
iwText.replaceRawString(VLC->generaltexth->colors[m13489val]);
|
iwText.replaceLocalString(EMetaText::COLOR, m13489val);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -380,7 +380,7 @@ void CQuest::getRolloverText(MetaString &ms, bool onHover) const
|
|||||||
|
|
||||||
void CQuest::getCompletionText(MetaString &iwText) const
|
void CQuest::getCompletionText(MetaString &iwText) const
|
||||||
{
|
{
|
||||||
iwText.appendRawString(completedText);
|
iwText.appendRawString(completedText.toString());
|
||||||
switch(missionType)
|
switch(missionType)
|
||||||
{
|
{
|
||||||
case CQuest::MISSION_LEVEL:
|
case CQuest::MISSION_LEVEL:
|
||||||
@ -388,22 +388,22 @@ void CQuest::getCompletionText(MetaString &iwText) const
|
|||||||
iwText.replaceNumber(m13489val);
|
iwText.replaceNumber(m13489val);
|
||||||
break;
|
break;
|
||||||
case CQuest::MISSION_PRIMARY_STAT:
|
case CQuest::MISSION_PRIMARY_STAT:
|
||||||
if (vstd::contains (completedText,'%')) //there's one case when there's nothing to replace
|
{
|
||||||
|
MetaString loot;
|
||||||
|
assert(m2stats.size() <= 4);
|
||||||
|
for (int i = 0; i < m2stats.size(); ++i)
|
||||||
{
|
{
|
||||||
MetaString loot;
|
if (m2stats[i])
|
||||||
for (int i = 0; i < 4; ++i)
|
|
||||||
{
|
{
|
||||||
if (m2stats[i])
|
loot.appendRawString("%d %s");
|
||||||
{
|
loot.replaceNumber(m2stats[i]);
|
||||||
loot.appendRawString("%d %s");
|
loot.replaceRawString(VLC->generaltexth->primarySkillNames[i]);
|
||||||
loot.replaceNumber(m2stats[i]);
|
|
||||||
loot.replaceRawString(VLC->generaltexth->primarySkillNames[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!isCustomComplete)
|
|
||||||
iwText.replaceRawString(loot.buildList());
|
|
||||||
}
|
}
|
||||||
|
if (!isCustomComplete)
|
||||||
|
iwText.replaceRawString(loot.buildList());
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case CQuest::MISSION_ART:
|
case CQuest::MISSION_ART:
|
||||||
{
|
{
|
||||||
MetaString loot;
|
MetaString loot;
|
||||||
@ -447,7 +447,7 @@ void CQuest::getCompletionText(MetaString &iwText) const
|
|||||||
case MISSION_KILL_HERO:
|
case MISSION_KILL_HERO:
|
||||||
case MISSION_KILL_CREATURE:
|
case MISSION_KILL_CREATURE:
|
||||||
if (!isCustomComplete)
|
if (!isCustomComplete)
|
||||||
addReplacements(iwText, completedText);
|
addReplacements(iwText, completedText.toString());
|
||||||
break;
|
break;
|
||||||
case MISSION_HERO:
|
case MISSION_HERO:
|
||||||
if (!isCustomComplete)
|
if (!isCustomComplete)
|
||||||
@ -470,9 +470,9 @@ void CQuest::serializeJson(JsonSerializeFormat & handler, const std::string & fi
|
|||||||
{
|
{
|
||||||
auto q = handler.enterStruct(fieldName);
|
auto q = handler.enterStruct(fieldName);
|
||||||
|
|
||||||
handler.serializeString("firstVisitText", firstVisitText);
|
handler.serializeStruct("firstVisitText", firstVisitText);
|
||||||
handler.serializeString("nextVisitText", nextVisitText);
|
handler.serializeStruct("nextVisitText", nextVisitText);
|
||||||
handler.serializeString("completedText", completedText);
|
handler.serializeStruct("completedText", completedText);
|
||||||
|
|
||||||
if(!handler.saving)
|
if(!handler.saving)
|
||||||
{
|
{
|
||||||
@ -589,16 +589,16 @@ void CGSeerHut::initObj(CRandomGenerator & rand)
|
|||||||
std::string questName = quest->missionName(quest->missionType);
|
std::string questName = quest->missionName(quest->missionType);
|
||||||
|
|
||||||
if(!quest->isCustomFirst)
|
if(!quest->isCustomFirst)
|
||||||
quest->firstVisitText = VLC->generaltexth->translate("core.seerhut.quest." + questName + "." + quest->missionState(0), quest->textOption);
|
quest->firstVisitText.appendTextID(TextIdentifier("core", "seerhut", "quest", questName, quest->missionState(0), quest->textOption).get());
|
||||||
if(!quest->isCustomNext)
|
if(!quest->isCustomNext)
|
||||||
quest->nextVisitText = VLC->generaltexth->translate("core.seerhut.quest." + questName + "." + quest->missionState(1), quest->textOption);
|
quest->firstVisitText.appendTextID(TextIdentifier("core", "seerhut", "quest", questName, quest->missionState(1), quest->textOption).get());
|
||||||
if(!quest->isCustomComplete)
|
if(!quest->isCustomComplete)
|
||||||
quest->completedText = VLC->generaltexth->translate("core.seerhut.quest." + questName + "." + quest->missionState(2), quest->textOption);
|
quest->firstVisitText.appendTextID(TextIdentifier("core", "seerhut", "quest", questName, quest->missionState(2), quest->textOption).get());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
quest->progress = CQuest::COMPLETE;
|
quest->progress = CQuest::COMPLETE;
|
||||||
quest->firstVisitText = VLC->generaltexth->seerEmpty[quest->completedOption];
|
quest->firstVisitText.appendTextID(TextIdentifier("core", "seehut", "empty", quest->completedOption).get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -632,14 +632,17 @@ void CQuest::addReplacements(MetaString &out, const std::string &base) const
|
|||||||
switch(missionType)
|
switch(missionType)
|
||||||
{
|
{
|
||||||
case MISSION_KILL_CREATURE:
|
case MISSION_KILL_CREATURE:
|
||||||
out.replaceCreatureName(stackToKill);
|
if(stackToKill.type)
|
||||||
if (std::count(base.begin(), base.end(), '%') == 2) //say where is placed monster
|
|
||||||
{
|
{
|
||||||
out.replaceRawString(VLC->generaltexth->arraytxt[147+stackDirection]);
|
out.replaceCreatureName(stackToKill);
|
||||||
|
if (std::count(base.begin(), base.end(), '%') == 2) //say where is placed monster
|
||||||
|
{
|
||||||
|
out.replaceRawString(VLC->generaltexth->arraytxt[147+stackDirection]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MISSION_KILL_HERO:
|
case MISSION_KILL_HERO:
|
||||||
out.replaceRawString(heroName);
|
out.replaceTextID(heroName);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include "CRewardableObject.h"
|
#include "CRewardableObject.h"
|
||||||
#include "../ResourceSet.h"
|
#include "../ResourceSet.h"
|
||||||
|
#include "../MetaString.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
@ -70,7 +71,7 @@ public:
|
|||||||
std::string heroName; //backup of hero name
|
std::string heroName; //backup of hero name
|
||||||
si32 heroPortrait;
|
si32 heroPortrait;
|
||||||
|
|
||||||
std::string firstVisitText, nextVisitText, completedText;
|
MetaString firstVisitText, nextVisitText, completedText;
|
||||||
bool isCustomFirst;
|
bool isCustomFirst;
|
||||||
bool isCustomNext;
|
bool isCustomNext;
|
||||||
bool isCustomComplete;
|
bool isCustomComplete;
|
||||||
|
@ -268,7 +268,7 @@ void CGResource::onHeroVisit( const CGHeroInstance * h ) const
|
|||||||
{
|
{
|
||||||
BlockingDialog ynd(true,false);
|
BlockingDialog ynd(true,false);
|
||||||
ynd.player = h->getOwner();
|
ynd.player = h->getOwner();
|
||||||
ynd.text.appendRawString(message);
|
ynd.text = message;
|
||||||
cb->showBlockingDialog(&ynd);
|
cb->showBlockingDialog(&ynd);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -288,7 +288,7 @@ void CGResource::collectRes(const PlayerColor & player) const
|
|||||||
if(!message.empty())
|
if(!message.empty())
|
||||||
{
|
{
|
||||||
sii.type = EInfoWindowMode::AUTO;
|
sii.type = EInfoWindowMode::AUTO;
|
||||||
sii.text.appendRawString(message);
|
sii.text = message;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -320,7 +320,7 @@ void CGResource::serializeJsonOptions(JsonSerializeFormat & handler)
|
|||||||
if(!handler.saving && !handler.getCurrent()["guards"].Vector().empty())
|
if(!handler.saving && !handler.getCurrent()["guards"].Vector().empty())
|
||||||
CCreatureSet::serializeJson(handler, "guards", 7);
|
CCreatureSet::serializeJson(handler, "guards", 7);
|
||||||
handler.serializeInt("amount", amount, 0);
|
handler.serializeInt("amount", amount, 0);
|
||||||
handler.serializeString("guardMessage", message);
|
handler.serializeStruct("guardMessage", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CGTeleport::isEntrance() const
|
bool CGTeleport::isEntrance() const
|
||||||
@ -728,8 +728,8 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const
|
|||||||
case Obj::ARTIFACT:
|
case Obj::ARTIFACT:
|
||||||
{
|
{
|
||||||
iw.components.emplace_back(Component::EComponentType::ARTIFACT, subID, 0, 0);
|
iw.components.emplace_back(Component::EComponentType::ARTIFACT, subID, 0, 0);
|
||||||
if(message.length())
|
if(!message.empty())
|
||||||
iw.text.appendRawString(message);
|
iw.text = message;
|
||||||
else
|
else
|
||||||
iw.text.appendLocalString(EMetaText::ART_EVNTS, subID);
|
iw.text.appendLocalString(EMetaText::ART_EVNTS, subID);
|
||||||
}
|
}
|
||||||
@ -738,8 +738,8 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const
|
|||||||
{
|
{
|
||||||
int spellID = storedArtifact->getScrollSpellID();
|
int spellID = storedArtifact->getScrollSpellID();
|
||||||
iw.components.emplace_back(Component::EComponentType::SPELL, spellID, 0, 0);
|
iw.components.emplace_back(Component::EComponentType::SPELL, spellID, 0, 0);
|
||||||
if(message.length())
|
if(!message.empty())
|
||||||
iw.text.appendRawString(message);
|
iw.text = message;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT,135);
|
iw.text.appendLocalString(EMetaText::ADVOB_TXT,135);
|
||||||
@ -764,8 +764,8 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const
|
|||||||
{
|
{
|
||||||
BlockingDialog ynd(true,false);
|
BlockingDialog ynd(true,false);
|
||||||
ynd.player = h->getOwner();
|
ynd.player = h->getOwner();
|
||||||
if(message.length())
|
if(!message.empty())
|
||||||
ynd.text.appendRawString(message);
|
ynd.text = message;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// TODO: Guard text is more complex in H3, see mantis issue 2325 for details
|
// TODO: Guard text is more complex in H3, see mantis issue 2325 for details
|
||||||
@ -779,11 +779,11 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const
|
|||||||
break;
|
break;
|
||||||
case Obj::SPELL_SCROLL:
|
case Obj::SPELL_SCROLL:
|
||||||
{
|
{
|
||||||
if(message.length())
|
if(!message.empty())
|
||||||
{
|
{
|
||||||
BlockingDialog ynd(true,false);
|
BlockingDialog ynd(true,false);
|
||||||
ynd.player = h->getOwner();
|
ynd.player = h->getOwner();
|
||||||
ynd.text.appendRawString(message);
|
ynd.text = message;
|
||||||
cb->showBlockingDialog(&ynd);
|
cb->showBlockingDialog(&ynd);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -828,7 +828,7 @@ void CGArtifact::afterAddToMap(CMap * map)
|
|||||||
|
|
||||||
void CGArtifact::serializeJsonOptions(JsonSerializeFormat& handler)
|
void CGArtifact::serializeJsonOptions(JsonSerializeFormat& handler)
|
||||||
{
|
{
|
||||||
handler.serializeString("guardMessage", message);
|
handler.serializeStruct("guardMessage", message);
|
||||||
CArmedInstance::serializeJsonOptions(handler);
|
CArmedInstance::serializeJsonOptions(handler);
|
||||||
if(!handler.saving && !handler.getCurrent()["guards"].Vector().empty())
|
if(!handler.saving && !handler.getCurrent()["guards"].Vector().empty())
|
||||||
CCreatureSet::serializeJson(handler, "guards", 7);
|
CCreatureSet::serializeJson(handler, "guards", 7);
|
||||||
@ -1046,7 +1046,7 @@ void CGSignBottle::initObj(CRandomGenerator & rand)
|
|||||||
{
|
{
|
||||||
auto vector = VLC->generaltexth->findStringsWithPrefix("core.randsign");
|
auto vector = VLC->generaltexth->findStringsWithPrefix("core.randsign");
|
||||||
std::string messageIdentifier = *RandomGeneratorUtil::nextItem(vector, rand);
|
std::string messageIdentifier = *RandomGeneratorUtil::nextItem(vector, rand);
|
||||||
message = VLC->generaltexth->translate(messageIdentifier);
|
message.appendTextID(TextIdentifier("core", "randsign", messageIdentifier).get());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ID == Obj::OCEAN_BOTTLE)
|
if(ID == Obj::OCEAN_BOTTLE)
|
||||||
@ -1059,7 +1059,7 @@ void CGSignBottle::onHeroVisit( const CGHeroInstance * h ) const
|
|||||||
{
|
{
|
||||||
InfoWindow iw;
|
InfoWindow iw;
|
||||||
iw.player = h->getOwner();
|
iw.player = h->getOwner();
|
||||||
iw.text.appendRawString(message);
|
iw.text = message;
|
||||||
cb->showInfoDialog(&iw);
|
cb->showInfoDialog(&iw);
|
||||||
|
|
||||||
if(ID == Obj::OCEAN_BOTTLE)
|
if(ID == Obj::OCEAN_BOTTLE)
|
||||||
@ -1068,7 +1068,7 @@ void CGSignBottle::onHeroVisit( const CGHeroInstance * h ) const
|
|||||||
|
|
||||||
void CGSignBottle::serializeJsonOptions(JsonSerializeFormat& handler)
|
void CGSignBottle::serializeJsonOptions(JsonSerializeFormat& handler)
|
||||||
{
|
{
|
||||||
handler.serializeString("text", message);
|
handler.serializeStruct("text", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGScholar::onHeroVisit( const CGHeroInstance * h ) const
|
void CGScholar::onHeroVisit( const CGHeroInstance * h ) const
|
||||||
|
@ -43,7 +43,7 @@ public:
|
|||||||
class DLL_LINKAGE CGSignBottle : public CGObjectInstance //signs and ocean bottles
|
class DLL_LINKAGE CGSignBottle : public CGObjectInstance //signs and ocean bottles
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::string message;
|
MetaString message;
|
||||||
|
|
||||||
void onHeroVisit(const CGHeroInstance * h) const override;
|
void onHeroVisit(const CGHeroInstance * h) const override;
|
||||||
void initObj(CRandomGenerator & rand) override;
|
void initObj(CRandomGenerator & rand) override;
|
||||||
@ -119,7 +119,7 @@ class DLL_LINKAGE CGArtifact : public CArmedInstance
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CArtifactInstance * storedArtifact = nullptr;
|
CArtifactInstance * storedArtifact = nullptr;
|
||||||
std::string message;
|
MetaString message;
|
||||||
|
|
||||||
void onHeroVisit(const CGHeroInstance * h) const override;
|
void onHeroVisit(const CGHeroInstance * h) const override;
|
||||||
void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
|
void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
|
||||||
@ -149,7 +149,7 @@ public:
|
|||||||
static constexpr ui32 RANDOM_AMOUNT = 0;
|
static constexpr ui32 RANDOM_AMOUNT = 0;
|
||||||
ui32 amount = RANDOM_AMOUNT; //0 if random
|
ui32 amount = RANDOM_AMOUNT; //0 if random
|
||||||
|
|
||||||
std::string message;
|
MetaString message;
|
||||||
|
|
||||||
void onHeroVisit(const CGHeroInstance * h) const override;
|
void onHeroVisit(const CGHeroInstance * h) const override;
|
||||||
void initObj(CRandomGenerator & rand) override;
|
void initObj(CRandomGenerator & rand) override;
|
||||||
|
@ -32,7 +32,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
|||||||
void Rumor::serializeJson(JsonSerializeFormat & handler)
|
void Rumor::serializeJson(JsonSerializeFormat & handler)
|
||||||
{
|
{
|
||||||
handler.serializeString("name", name);
|
handler.serializeString("name", name);
|
||||||
handler.serializeString("text", text);
|
handler.serializeStruct("text", text);
|
||||||
}
|
}
|
||||||
|
|
||||||
DisposedHero::DisposedHero() : heroId(0), portrait(255)
|
DisposedHero::DisposedHero() : heroId(0), portrait(255)
|
||||||
@ -59,7 +59,7 @@ bool CMapEvent::earlierThanOrEqual(const CMapEvent & other) const
|
|||||||
void CMapEvent::serializeJson(JsonSerializeFormat & handler)
|
void CMapEvent::serializeJson(JsonSerializeFormat & handler)
|
||||||
{
|
{
|
||||||
handler.serializeString("name", name);
|
handler.serializeString("name", name);
|
||||||
handler.serializeString("message", message);
|
handler.serializeStruct("message", message);
|
||||||
handler.serializeInt("players", players);
|
handler.serializeInt("players", players);
|
||||||
handler.serializeInt("humanAffected", humanAffected);
|
handler.serializeInt("humanAffected", humanAffected);
|
||||||
handler.serializeInt("computerAffected", computerAffected);
|
handler.serializeInt("computerAffected", computerAffected);
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CMapHeader.h"
|
#include "CMapHeader.h"
|
||||||
|
#include "../MetaString.h"
|
||||||
#include "../mapObjects/MiscObjects.h" // To serialize static props
|
#include "../mapObjects/MiscObjects.h" // To serialize static props
|
||||||
#include "../mapObjects/CQuest.h" // To serialize static props
|
#include "../mapObjects/CQuest.h" // To serialize static props
|
||||||
#include "../mapObjects/CGTownInstance.h" // To serialize static props
|
#include "../mapObjects/CGTownInstance.h" // To serialize static props
|
||||||
@ -36,7 +37,7 @@ struct TeleportChannel;
|
|||||||
struct DLL_LINKAGE Rumor
|
struct DLL_LINKAGE Rumor
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string text;
|
MetaString text;
|
||||||
|
|
||||||
Rumor() = default;
|
Rumor() = default;
|
||||||
~Rumor() = default;
|
~Rumor() = default;
|
||||||
|
@ -10,9 +10,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
|
||||||
|
|
||||||
#include "../ResourceSet.h"
|
#include "../ResourceSet.h"
|
||||||
|
#include "../MetaString.h"
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
class TerrainType;
|
class TerrainType;
|
||||||
class RiverType;
|
class RiverType;
|
||||||
@ -33,7 +34,7 @@ public:
|
|||||||
bool earlierThanOrEqual(const CMapEvent & other) const;
|
bool earlierThanOrEqual(const CMapEvent & other) const;
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string message;
|
MetaString message;
|
||||||
TResources resources;
|
TResources resources;
|
||||||
ui8 players; // affected players, bit field?
|
ui8 players; // affected players, bit field?
|
||||||
ui8 humanAffected;
|
ui8 humanAffected;
|
||||||
|
@ -15,7 +15,9 @@
|
|||||||
#include "../VCMI_Lib.h"
|
#include "../VCMI_Lib.h"
|
||||||
#include "../CTownHandler.h"
|
#include "../CTownHandler.h"
|
||||||
#include "../CGeneralTextHandler.h"
|
#include "../CGeneralTextHandler.h"
|
||||||
|
#include "../modding/CModHandler.h"
|
||||||
#include "../CHeroHandler.h"
|
#include "../CHeroHandler.h"
|
||||||
|
#include "../Languages.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
@ -62,7 +64,7 @@ bool PlayerInfo::canAnyonePlay() const
|
|||||||
|
|
||||||
bool PlayerInfo::hasCustomMainHero() const
|
bool PlayerInfo::hasCustomMainHero() const
|
||||||
{
|
{
|
||||||
return !mainCustomHeroName.empty() && mainCustomHeroPortrait != -1;
|
return !mainCustomHeroNameTextId.empty() && mainCustomHeroPortrait != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
EventCondition::EventCondition(EWinLoseType condition):
|
EventCondition::EventCondition(EWinLoseType condition):
|
||||||
@ -127,6 +129,12 @@ CMapHeader::CMapHeader() : version(EMapFormat::VCMI), height(72), width(72),
|
|||||||
setupEvents();
|
setupEvents();
|
||||||
allowedHeroes = VLC->heroh->getDefaultAllowed();
|
allowedHeroes = VLC->heroh->getDefaultAllowed();
|
||||||
players.resize(PlayerColor::PLAYER_LIMIT_I);
|
players.resize(PlayerColor::PLAYER_LIMIT_I);
|
||||||
|
VLC->generaltexth->addSubContainer(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
CMapHeader::~CMapHeader()
|
||||||
|
{
|
||||||
|
VLC->generaltexth->removeSubContainer(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
ui8 CMapHeader::levels() const
|
ui8 CMapHeader::levels() const
|
||||||
@ -134,4 +142,74 @@ ui8 CMapHeader::levels() const
|
|||||||
return (twoLevel ? 2 : 1);
|
return (twoLevel ? 2 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CMapHeader::registerMapStrings()
|
||||||
|
{
|
||||||
|
//get supported languages. Assuming that translation containing most strings is the base language
|
||||||
|
std::set<std::string> mapLanguages, mapBaseLanguages;
|
||||||
|
int maxStrings = 0;
|
||||||
|
for(auto & translation : translations.Struct())
|
||||||
|
{
|
||||||
|
if(translation.first.empty() || !translation.second.isStruct() || translation.second.Struct().empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(translation.second.Struct().size() > maxStrings)
|
||||||
|
maxStrings = translation.second.Struct().size();
|
||||||
|
mapLanguages.insert(translation.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(maxStrings == 0 || mapBaseLanguages.empty())
|
||||||
|
{
|
||||||
|
logGlobal->info("Map %s doesn't have any supported translation", name.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//identifying base languages
|
||||||
|
for(auto & translation : translations.Struct())
|
||||||
|
{
|
||||||
|
if(translation.second.isStruct() && translation.second.Struct().size() == maxStrings)
|
||||||
|
mapBaseLanguages.insert(translation.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string baseLanguage, language;
|
||||||
|
//english is preferrable as base language
|
||||||
|
if(mapBaseLanguages.count(Languages::getLanguageOptions(Languages::ELanguages::ENGLISH).identifier))
|
||||||
|
baseLanguage = Languages::getLanguageOptions(Languages::ELanguages::ENGLISH).identifier;
|
||||||
|
else
|
||||||
|
baseLanguage = *mapBaseLanguages.begin();
|
||||||
|
|
||||||
|
if(mapBaseLanguages.count(CGeneralTextHandler::getPreferredLanguage()))
|
||||||
|
{
|
||||||
|
language = CGeneralTextHandler::getPreferredLanguage(); //preferred language is base language - use it
|
||||||
|
baseLanguage = language;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(mapLanguages.count(CGeneralTextHandler::getPreferredLanguage()))
|
||||||
|
language = CGeneralTextHandler::getPreferredLanguage();
|
||||||
|
else
|
||||||
|
language = baseLanguage; //preferred language is not supported, use base language
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!language.empty());
|
||||||
|
|
||||||
|
JsonNode data = translations[baseLanguage];
|
||||||
|
if(language != baseLanguage)
|
||||||
|
JsonUtils::mergeCopy(data, translations[language]);
|
||||||
|
|
||||||
|
for(auto & s : data.Struct())
|
||||||
|
registerString("map", TextIdentifier(s.first), s.second.String(), language);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string mapRegisterLocalizedString(const std::string & modContext, CMapHeader & mapHeader, const TextIdentifier & UID, const std::string & localized)
|
||||||
|
{
|
||||||
|
return mapRegisterLocalizedString(modContext, mapHeader, UID, localized, VLC->modh->getModLanguage(modContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string mapRegisterLocalizedString(const std::string & modContext, CMapHeader & mapHeader, const TextIdentifier & UID, const std::string & localized, const std::string & language)
|
||||||
|
{
|
||||||
|
mapHeader.registerString(modContext, UID, localized, language);
|
||||||
|
mapHeader.translations.Struct()[language].Struct()[UID.get()].String() = localized;
|
||||||
|
return UID.get();
|
||||||
|
}
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "../LogicalExpression.h"
|
#include "../LogicalExpression.h"
|
||||||
#include "../int3.h"
|
#include "../int3.h"
|
||||||
#include "../MetaString.h"
|
#include "../MetaString.h"
|
||||||
|
#include "../CGeneralTextHandler.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
@ -63,7 +64,7 @@ struct DLL_LINKAGE PlayerInfo
|
|||||||
bool hasRandomHero;
|
bool hasRandomHero;
|
||||||
/// The default value is -1.
|
/// The default value is -1.
|
||||||
si32 mainCustomHeroPortrait;
|
si32 mainCustomHeroPortrait;
|
||||||
std::string mainCustomHeroName;
|
std::string mainCustomHeroNameTextId;
|
||||||
/// ID of custom hero (only if portrait and hero name are set, otherwise unpredicted value), -1 if none (not always -1)
|
/// ID of custom hero (only if portrait and hero name are set, otherwise unpredicted value), -1 if none (not always -1)
|
||||||
si32 mainCustomHeroId;
|
si32 mainCustomHeroId;
|
||||||
|
|
||||||
@ -84,7 +85,7 @@ struct DLL_LINKAGE PlayerInfo
|
|||||||
h & allowedFactions;
|
h & allowedFactions;
|
||||||
h & isFactionRandom;
|
h & isFactionRandom;
|
||||||
h & mainCustomHeroPortrait;
|
h & mainCustomHeroPortrait;
|
||||||
h & mainCustomHeroName;
|
h & mainCustomHeroNameTextId;
|
||||||
h & heroesNames;
|
h & heroesNames;
|
||||||
h & hasMainTown;
|
h & hasMainTown;
|
||||||
h & generateHeroAtMainTown;
|
h & generateHeroAtMainTown;
|
||||||
@ -199,7 +200,7 @@ struct DLL_LINKAGE TriggeredEvent
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// The map header holds information about loss/victory condition,map format, version, players, height, width,...
|
/// The map header holds information about loss/victory condition,map format, version, players, height, width,...
|
||||||
class DLL_LINKAGE CMapHeader
|
class DLL_LINKAGE CMapHeader: public TextLocalizationContainer
|
||||||
{
|
{
|
||||||
void setupEvents();
|
void setupEvents();
|
||||||
public:
|
public:
|
||||||
@ -213,7 +214,7 @@ public:
|
|||||||
static const int MAP_SIZE_GIANT = 252;
|
static const int MAP_SIZE_GIANT = 252;
|
||||||
|
|
||||||
CMapHeader();
|
CMapHeader();
|
||||||
virtual ~CMapHeader() = default;
|
virtual ~CMapHeader();
|
||||||
|
|
||||||
ui8 levels() const;
|
ui8 levels() const;
|
||||||
|
|
||||||
@ -223,8 +224,8 @@ public:
|
|||||||
si32 height; /// The default value is 72.
|
si32 height; /// The default value is 72.
|
||||||
si32 width; /// The default value is 72.
|
si32 width; /// The default value is 72.
|
||||||
bool twoLevel; /// The default value is true.
|
bool twoLevel; /// The default value is true.
|
||||||
std::string name;
|
MetaString name;
|
||||||
std::string description;
|
MetaString description;
|
||||||
ui8 difficulty; /// The default value is 1 representing a normal map difficulty.
|
ui8 difficulty; /// The default value is 1 representing a normal map difficulty.
|
||||||
/// Specifies the maximum level to reach for a hero. A value of 0 states that there is no
|
/// Specifies the maximum level to reach for a hero. A value of 0 states that there is no
|
||||||
/// maximum level for heroes. This is the default value.
|
/// maximum level for heroes. This is the default value.
|
||||||
@ -244,10 +245,16 @@ public:
|
|||||||
|
|
||||||
/// "main quests" of the map that describe victory and loss conditions
|
/// "main quests" of the map that describe victory and loss conditions
|
||||||
std::vector<TriggeredEvent> triggeredEvents;
|
std::vector<TriggeredEvent> triggeredEvents;
|
||||||
|
|
||||||
|
/// translations for map to be transferred over network
|
||||||
|
JsonNode translations;
|
||||||
|
|
||||||
|
void registerMapStrings();
|
||||||
|
|
||||||
template <typename Handler>
|
template <typename Handler>
|
||||||
void serialize(Handler & h, const int Version)
|
void serialize(Handler & h, const int Version)
|
||||||
{
|
{
|
||||||
|
h & static_cast<TextLocalizationContainer&>(*this);
|
||||||
h & version;
|
h & version;
|
||||||
h & mods;
|
h & mods;
|
||||||
h & name;
|
h & name;
|
||||||
@ -267,7 +274,14 @@ public:
|
|||||||
h & victoryIconIndex;
|
h & victoryIconIndex;
|
||||||
h & defeatMessage;
|
h & defeatMessage;
|
||||||
h & defeatIconIndex;
|
h & defeatIconIndex;
|
||||||
|
h & translations;
|
||||||
|
if(!h.saving)
|
||||||
|
registerMapStrings();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// wrapper functions to register string into the map and stores its translation
|
||||||
|
std::string DLL_LINKAGE mapRegisterLocalizedString(const std::string & modContext, CMapHeader & mapHeader, const TextIdentifier & UID, const std::string & localized);
|
||||||
|
std::string DLL_LINKAGE mapRegisterLocalizedString(const std::string & modContext, CMapHeader & mapHeader, const TextIdentifier & UID, const std::string & localized, const std::string & language);
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
@ -100,12 +100,12 @@ void CMapInfo::countPlayers()
|
|||||||
amountOfHumanPlayersInSave++;
|
amountOfHumanPlayersInSave++;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CMapInfo::getName() const
|
std::string CMapInfo::getNameTranslated() const
|
||||||
{
|
{
|
||||||
if(campaign && !campaign->getName().empty())
|
if(campaign && !campaign->getNameTranslated().empty())
|
||||||
return campaign->getName();
|
return campaign->getNameTranslated();
|
||||||
else if(mapHeader && mapHeader->name.length())
|
else if(mapHeader && !mapHeader->name.empty())
|
||||||
return mapHeader->name;
|
return mapHeader->name.toString();
|
||||||
else
|
else
|
||||||
return VLC->generaltexth->allTexts[508];
|
return VLC->generaltexth->allTexts[508];
|
||||||
}
|
}
|
||||||
@ -121,16 +121,16 @@ std::string CMapInfo::getNameForList() const
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return getName();
|
return getNameTranslated();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CMapInfo::getDescription() const
|
std::string CMapInfo::getDescriptionTranslated() const
|
||||||
{
|
{
|
||||||
if(campaign)
|
if(campaign)
|
||||||
return campaign->getDescription();
|
return campaign->getDescriptionTranslated();
|
||||||
else
|
else
|
||||||
return mapHeader->description;
|
return mapHeader->description.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
int CMapInfo::getMapSizeIconId() const
|
int CMapInfo::getMapSizeIconId() const
|
||||||
|
@ -49,10 +49,10 @@ public:
|
|||||||
void saveInit(const ResourcePath & file);
|
void saveInit(const ResourcePath & file);
|
||||||
void campaignInit();
|
void campaignInit();
|
||||||
void countPlayers();
|
void countPlayers();
|
||||||
// TODO: Those must be on client-side
|
|
||||||
std::string getName() const;
|
std::string getNameTranslated() const;
|
||||||
std::string getNameForList() const;
|
std::string getNameForList() const;
|
||||||
std::string getDescription() const;
|
std::string getDescriptionTranslated() const;
|
||||||
int getMapSizeIconId() const;
|
int getMapSizeIconId() const;
|
||||||
int getMapSizeFormatIconId() const;
|
int getMapSizeFormatIconId() const;
|
||||||
std::string getMapSizeName() const;
|
std::string getMapSizeName() const;
|
||||||
|
@ -180,8 +180,8 @@ void CMapLoaderH3M::readHeader()
|
|||||||
mapHeader->areAnyPlayers = reader->readBool();
|
mapHeader->areAnyPlayers = reader->readBool();
|
||||||
mapHeader->height = mapHeader->width = reader->readInt32();
|
mapHeader->height = mapHeader->width = reader->readInt32();
|
||||||
mapHeader->twoLevel = reader->readBool();
|
mapHeader->twoLevel = reader->readBool();
|
||||||
mapHeader->name = readLocalizedString("header.name");
|
mapHeader->name.appendTextID(readLocalizedString("header.name"));
|
||||||
mapHeader->description = readLocalizedString("header.description");
|
mapHeader->description.appendTextID(readLocalizedString("header.description"));
|
||||||
mapHeader->difficulty = reader->readInt8();
|
mapHeader->difficulty = reader->readInt8();
|
||||||
|
|
||||||
if(features.levelAB)
|
if(features.levelAB)
|
||||||
@ -253,7 +253,7 @@ void CMapLoaderH3M::readPlayerInfo()
|
|||||||
if(playerInfo.mainCustomHeroId != -1)
|
if(playerInfo.mainCustomHeroId != -1)
|
||||||
{
|
{
|
||||||
playerInfo.mainCustomHeroPortrait = reader->readHeroPortrait();
|
playerInfo.mainCustomHeroPortrait = reader->readHeroPortrait();
|
||||||
playerInfo.mainCustomHeroName = readLocalizedString(TextIdentifier("header", "player", i, "mainHeroName"));
|
playerInfo.mainCustomHeroNameTextId = readLocalizedString(TextIdentifier("header", "player", i, "mainHeroName"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(features.levelAB)
|
if(features.levelAB)
|
||||||
@ -807,7 +807,7 @@ void CMapLoaderH3M::readRumors()
|
|||||||
{
|
{
|
||||||
Rumor ourRumor;
|
Rumor ourRumor;
|
||||||
ourRumor.name = readBasicString();
|
ourRumor.name = readBasicString();
|
||||||
ourRumor.text = readLocalizedString(TextIdentifier("header", "rumor", it, "text"));
|
ourRumor.text.appendTextID(readLocalizedString(TextIdentifier("header", "rumor", it, "text")));
|
||||||
map->rumors.push_back(ourRumor);
|
map->rumors.push_back(ourRumor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -860,7 +860,7 @@ void CMapLoaderH3M::readPredefinedHeroes()
|
|||||||
|
|
||||||
bool hasCustomBio = reader->readBool();
|
bool hasCustomBio = reader->readBool();
|
||||||
if(hasCustomBio)
|
if(hasCustomBio)
|
||||||
hero->biographyCustom = readLocalizedString(TextIdentifier("heroes", heroID, "biography"));
|
hero->biographyCustomTextId = readLocalizedString(TextIdentifier("heroes", heroID, "biography"));
|
||||||
|
|
||||||
// 0xFF is default, 00 male, 01 female
|
// 0xFF is default, 00 male, 01 female
|
||||||
hero->gender = static_cast<EHeroGender>(reader->readUInt8());
|
hero->gender = static_cast<EHeroGender>(reader->readUInt8());
|
||||||
@ -1099,7 +1099,7 @@ CGObjectInstance * CMapLoaderH3M::readMonster(const int3 & mapPosition, const Ob
|
|||||||
bool hasMessage = reader->readBool();
|
bool hasMessage = reader->readBool();
|
||||||
if(hasMessage)
|
if(hasMessage)
|
||||||
{
|
{
|
||||||
object->message = readLocalizedString(TextIdentifier("monster", mapPosition.x, mapPosition.y, mapPosition.z, "message"));
|
object->message.appendTextID(readLocalizedString(TextIdentifier("monster", mapPosition.x, mapPosition.y, mapPosition.z, "message")));
|
||||||
reader->readResourses(object->resources);
|
reader->readResourses(object->resources);
|
||||||
object->gainedArtifact = reader->readArtifact();
|
object->gainedArtifact = reader->readArtifact();
|
||||||
}
|
}
|
||||||
@ -1135,7 +1135,7 @@ CGObjectInstance * CMapLoaderH3M::readMonster(const int3 & mapPosition, const Ob
|
|||||||
CGObjectInstance * CMapLoaderH3M::readSign(const int3 & mapPosition)
|
CGObjectInstance * CMapLoaderH3M::readSign(const int3 & mapPosition)
|
||||||
{
|
{
|
||||||
auto * object = new CGSignBottle();
|
auto * object = new CGSignBottle();
|
||||||
object->message = readLocalizedString(TextIdentifier("sign", mapPosition.x, mapPosition.y, mapPosition.z, "message"));
|
object->message.appendTextID(readLocalizedString(TextIdentifier("sign", mapPosition.x, mapPosition.y, mapPosition.z, "message")));
|
||||||
reader->skipZero(4);
|
reader->skipZero(4);
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
@ -1685,7 +1685,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(const int3 & mapPosition, const Objec
|
|||||||
{
|
{
|
||||||
if(elem.heroId.getNum() == object->subID)
|
if(elem.heroId.getNum() == object->subID)
|
||||||
{
|
{
|
||||||
object->nameCustom = elem.name;
|
object->nameCustomTextId = elem.name;
|
||||||
object->portrait = elem.portrait;
|
object->portrait = elem.portrait;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1693,7 +1693,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(const int3 & mapPosition, const Objec
|
|||||||
|
|
||||||
bool hasName = reader->readBool();
|
bool hasName = reader->readBool();
|
||||||
if(hasName)
|
if(hasName)
|
||||||
object->nameCustom = readLocalizedString(TextIdentifier("heroes", object->subID, "name"));
|
object->nameCustomTextId = readLocalizedString(TextIdentifier("heroes", object->subID, "name"));
|
||||||
|
|
||||||
if(features.levelSOD)
|
if(features.levelSOD)
|
||||||
{
|
{
|
||||||
@ -1748,7 +1748,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(const int3 & mapPosition, const Objec
|
|||||||
{
|
{
|
||||||
bool hasCustomBiography = reader->readBool();
|
bool hasCustomBiography = reader->readBool();
|
||||||
if(hasCustomBiography)
|
if(hasCustomBiography)
|
||||||
object->biographyCustom = readLocalizedString(TextIdentifier("heroes", object->subID, "biography"));
|
object->biographyCustomTextId = readLocalizedString(TextIdentifier("heroes", object->subID, "biography"));
|
||||||
|
|
||||||
object->gender = static_cast<EHeroGender>(reader->readUInt8());
|
object->gender = static_cast<EHeroGender>(reader->readUInt8());
|
||||||
assert(object->gender == EHeroGender::MALE || object->gender == EHeroGender::FEMALE || object->gender == EHeroGender::DEFAULT);
|
assert(object->gender == EHeroGender::MALE || object->gender == EHeroGender::FEMALE || object->gender == EHeroGender::DEFAULT);
|
||||||
@ -2072,9 +2072,9 @@ void CMapLoaderH3M::readQuest(IQuestObject * guard, const int3 & position)
|
|||||||
}
|
}
|
||||||
|
|
||||||
guard->quest->lastDay = reader->readInt32();
|
guard->quest->lastDay = reader->readInt32();
|
||||||
guard->quest->firstVisitText = readLocalizedString(TextIdentifier("quest", position.x, position.y, position.z, "firstVisit"));
|
guard->quest->firstVisitText.appendTextID(readLocalizedString(TextIdentifier("quest", position.x, position.y, position.z, "firstVisit")));
|
||||||
guard->quest->nextVisitText = readLocalizedString(TextIdentifier("quest", position.x, position.y, position.z, "nextVisit"));
|
guard->quest->nextVisitText.appendTextID(readLocalizedString(TextIdentifier("quest", position.x, position.y, position.z, "nextVisit")));
|
||||||
guard->quest->completedText = readLocalizedString(TextIdentifier("quest", position.x, position.y, position.z, "completed"));
|
guard->quest->completedText.appendTextID(readLocalizedString(TextIdentifier("quest", position.x, position.y, position.z, "completed")));
|
||||||
guard->quest->isCustomFirst = !guard->quest->firstVisitText.empty();
|
guard->quest->isCustomFirst = !guard->quest->firstVisitText.empty();
|
||||||
guard->quest->isCustomNext = !guard->quest->nextVisitText.empty();
|
guard->quest->isCustomNext = !guard->quest->nextVisitText.empty();
|
||||||
guard->quest->isCustomComplete = !guard->quest->completedText.empty();
|
guard->quest->isCustomComplete = !guard->quest->completedText.empty();
|
||||||
@ -2094,7 +2094,7 @@ CGObjectInstance * CMapLoaderH3M::readTown(const int3 & position, std::shared_pt
|
|||||||
|
|
||||||
bool hasName = reader->readBool();
|
bool hasName = reader->readBool();
|
||||||
if(hasName)
|
if(hasName)
|
||||||
object->setNameTranslated(readLocalizedString(TextIdentifier("town", position.x, position.y, position.z, "name")));
|
object->setNameTextId(readLocalizedString(TextIdentifier("town", position.x, position.y, position.z, "name")));
|
||||||
|
|
||||||
bool hasGarrison = reader->readBool();
|
bool hasGarrison = reader->readBool();
|
||||||
if(hasGarrison)
|
if(hasGarrison)
|
||||||
@ -2155,7 +2155,7 @@ CGObjectInstance * CMapLoaderH3M::readTown(const int3 & position, std::shared_pt
|
|||||||
{
|
{
|
||||||
CCastleEvent event;
|
CCastleEvent event;
|
||||||
event.name = readBasicString();
|
event.name = readBasicString();
|
||||||
event.message = readLocalizedString(TextIdentifier("town", position.x, position.y, position.z, "event", eventID, "description"));
|
event.message.appendTextID(readLocalizedString(TextIdentifier("town", position.x, position.y, position.z, "event", eventID, "description")));
|
||||||
|
|
||||||
reader->readResourses(event.resources);
|
reader->readResourses(event.resources);
|
||||||
|
|
||||||
@ -2225,7 +2225,7 @@ void CMapLoaderH3M::readEvents()
|
|||||||
{
|
{
|
||||||
CMapEvent event;
|
CMapEvent event;
|
||||||
event.name = readBasicString();
|
event.name = readBasicString();
|
||||||
event.message = readLocalizedString(TextIdentifier("event", eventID, "description"));
|
event.message.appendTextID(readLocalizedString(TextIdentifier("event", eventID, "description")));
|
||||||
|
|
||||||
reader->readResourses(event.resources);
|
reader->readResourses(event.resources);
|
||||||
event.players = reader->readUInt8();
|
event.players = reader->readUInt8();
|
||||||
@ -2247,12 +2247,12 @@ void CMapLoaderH3M::readEvents()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMapLoaderH3M::readMessageAndGuards(std::string & message, CCreatureSet * guards, const int3 & position)
|
void CMapLoaderH3M::readMessageAndGuards(MetaString & message, CCreatureSet * guards, const int3 & position)
|
||||||
{
|
{
|
||||||
bool hasMessage = reader->readBool();
|
bool hasMessage = reader->readBool();
|
||||||
if(hasMessage)
|
if(hasMessage)
|
||||||
{
|
{
|
||||||
message = readLocalizedString(TextIdentifier("guards", position.x, position.y, position.z, "message"));
|
message.appendTextID(readLocalizedString(TextIdentifier("guards", position.x, position.y, position.z, "message")));
|
||||||
bool hasGuards = reader->readBool();
|
bool hasGuards = reader->readBool();
|
||||||
if(hasGuards)
|
if(hasGuards)
|
||||||
readCreatureSet(guards, 7);
|
readCreatureSet(guards, 7);
|
||||||
@ -2274,8 +2274,7 @@ std::string CMapLoaderH3M::readLocalizedString(const TextIdentifier & stringIden
|
|||||||
if(mapString.empty())
|
if(mapString.empty())
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
VLC->generaltexth->registerString(modName, fullIdentifier, mapString);
|
return mapRegisterLocalizedString(modName, *mapHeader, fullIdentifier, mapString);
|
||||||
return VLC->generaltexth->translate(fullIdentifier.get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMapLoaderH3M::afterRead()
|
void CMapLoaderH3M::afterRead()
|
||||||
|
@ -17,6 +17,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
|||||||
|
|
||||||
class CGHeroInstance;
|
class CGHeroInstance;
|
||||||
class MapReaderH3M;
|
class MapReaderH3M;
|
||||||
|
class MetaString;
|
||||||
class CArtifactInstance;
|
class CArtifactInstance;
|
||||||
class CGObjectInstance;
|
class CGObjectInstance;
|
||||||
class CGSeerHut;
|
class CGSeerHut;
|
||||||
@ -215,7 +216,7 @@ private:
|
|||||||
/**
|
/**
|
||||||
* read optional message and optional guards
|
* read optional message and optional guards
|
||||||
*/
|
*/
|
||||||
void readMessageAndGuards(std::string & message, CCreatureSet * guards, const int3 & position);
|
void readMessageAndGuards(MetaString & message, CCreatureSet * guards, const int3 & position);
|
||||||
|
|
||||||
/// reads string from input stream and converts it to unicode
|
/// reads string from input stream and converts it to unicode
|
||||||
std::string readBasicString();
|
std::string readBasicString();
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
#include "../constants/StringConstants.h"
|
#include "../constants/StringConstants.h"
|
||||||
#include "../serializer/JsonDeserializer.h"
|
#include "../serializer/JsonDeserializer.h"
|
||||||
#include "../serializer/JsonSerializer.h"
|
#include "../serializer/JsonSerializer.h"
|
||||||
|
#include "../Languages.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
@ -341,11 +342,12 @@ namespace TerrainDetail
|
|||||||
}
|
}
|
||||||
|
|
||||||
///CMapFormatJson
|
///CMapFormatJson
|
||||||
const int CMapFormatJson::VERSION_MAJOR = 1;
|
const int CMapFormatJson::VERSION_MAJOR = 2;
|
||||||
const int CMapFormatJson::VERSION_MINOR = 3;
|
const int CMapFormatJson::VERSION_MINOR = 0;
|
||||||
|
|
||||||
const std::string CMapFormatJson::HEADER_FILE_NAME = "header.json";
|
const std::string CMapFormatJson::HEADER_FILE_NAME = "header.json";
|
||||||
const std::string CMapFormatJson::OBJECTS_FILE_NAME = "objects.json";
|
const std::string CMapFormatJson::OBJECTS_FILE_NAME = "objects.json";
|
||||||
|
const std::string CMapFormatJson::TERRAIN_FILE_NAMES[2] = {"surface_terrain.json", "underground_terrain.json"};
|
||||||
|
|
||||||
CMapFormatJson::CMapFormatJson():
|
CMapFormatJson::CMapFormatJson():
|
||||||
fileVersionMajor(0), fileVersionMinor(0),
|
fileVersionMajor(0), fileVersionMinor(0),
|
||||||
@ -413,8 +415,8 @@ void CMapFormatJson::serializeAllowedFactions(JsonSerializeFormat & handler, std
|
|||||||
|
|
||||||
void CMapFormatJson::serializeHeader(JsonSerializeFormat & handler)
|
void CMapFormatJson::serializeHeader(JsonSerializeFormat & handler)
|
||||||
{
|
{
|
||||||
handler.serializeString("name", mapHeader->name);
|
handler.serializeStruct("name", mapHeader->name);
|
||||||
handler.serializeString("description", mapHeader->description);
|
handler.serializeStruct("description", mapHeader->description);
|
||||||
handler.serializeInt("heroLevelLimit", mapHeader->levelLimit, 0);
|
handler.serializeInt("heroLevelLimit", mapHeader->levelLimit, 0);
|
||||||
|
|
||||||
//todo: support arbitrary percentage
|
//todo: support arbitrary percentage
|
||||||
@ -424,10 +426,10 @@ void CMapFormatJson::serializeHeader(JsonSerializeFormat & handler)
|
|||||||
|
|
||||||
handler.serializeLIC("allowedHeroes", &HeroTypeID::decode, &HeroTypeID::encode, VLC->heroh->getDefaultAllowed(), mapHeader->allowedHeroes);
|
handler.serializeLIC("allowedHeroes", &HeroTypeID::decode, &HeroTypeID::encode, VLC->heroh->getDefaultAllowed(), mapHeader->allowedHeroes);
|
||||||
|
|
||||||
// handler.serializeString("victoryString", mapHeader->victoryMessage);
|
handler.serializeStruct("victoryMessage", mapHeader->victoryMessage);
|
||||||
handler.serializeInt("victoryIconIndex", mapHeader->victoryIconIndex);
|
handler.serializeInt("victoryIconIndex", mapHeader->victoryIconIndex);
|
||||||
|
|
||||||
// handler.serializeString("defeatString", mapHeader->defeatMessage);
|
handler.serializeStruct("defeatMessage", mapHeader->defeatMessage);
|
||||||
handler.serializeInt("defeatIconIndex", mapHeader->defeatIconIndex);
|
handler.serializeInt("defeatIconIndex", mapHeader->defeatIconIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -529,7 +531,7 @@ void CMapFormatJson::serializePlayerInfo(JsonSerializeFormat & handler)
|
|||||||
if(hero)
|
if(hero)
|
||||||
{
|
{
|
||||||
auto heroData = handler.enterStruct(hero->instanceName);
|
auto heroData = handler.enterStruct(hero->instanceName);
|
||||||
heroData->serializeString("name", hero->nameCustom);
|
heroData->serializeString("name", hero->nameCustomTextId);
|
||||||
|
|
||||||
if(hero->ID == Obj::HERO)
|
if(hero->ID == Obj::HERO)
|
||||||
{
|
{
|
||||||
@ -571,7 +573,7 @@ void CMapFormatJson::serializePlayerInfo(JsonSerializeFormat & handler)
|
|||||||
if(instanceName == info.mainHeroInstance)
|
if(instanceName == info.mainHeroInstance)
|
||||||
{
|
{
|
||||||
//this is main hero
|
//this is main hero
|
||||||
info.mainCustomHeroName = hname.heroName;
|
info.mainCustomHeroNameTextId = hname.heroName;
|
||||||
info.hasRandomHero = (hname.heroId == -1);
|
info.hasRandomHero = (hname.heroId == -1);
|
||||||
info.mainCustomHeroId = hname.heroId;
|
info.mainCustomHeroId = hname.heroId;
|
||||||
info.mainCustomHeroPortrait = -1;
|
info.mainCustomHeroPortrait = -1;
|
||||||
@ -905,6 +907,11 @@ std::unique_ptr<CMapHeader> CMapLoaderJson::loadMapHeader()
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CMapLoaderJson::isExistArchive(const std::string & archiveFilename)
|
||||||
|
{
|
||||||
|
return loader.existsResource(JsonPath::builtin(archiveFilename));
|
||||||
|
}
|
||||||
|
|
||||||
JsonNode CMapLoaderJson::getFromArchive(const std::string & archiveFilename)
|
JsonNode CMapLoaderJson::getFromArchive(const std::string & archiveFilename)
|
||||||
{
|
{
|
||||||
JsonPath resource = JsonPath::builtin(archiveFilename);
|
JsonPath resource = JsonPath::builtin(archiveFilename);
|
||||||
@ -937,7 +944,7 @@ void CMapLoaderJson::readHeader(const bool complete)
|
|||||||
|
|
||||||
fileVersionMajor = static_cast<int>(header["versionMajor"].Integer());
|
fileVersionMajor = static_cast<int>(header["versionMajor"].Integer());
|
||||||
|
|
||||||
if(fileVersionMajor != VERSION_MAJOR)
|
if(fileVersionMajor > VERSION_MAJOR)
|
||||||
{
|
{
|
||||||
logGlobal->error("Unsupported map format version: %d", fileVersionMajor);
|
logGlobal->error("Unsupported map format version: %d", fileVersionMajor);
|
||||||
throw std::runtime_error("Unsupported map format version");
|
throw std::runtime_error("Unsupported map format version");
|
||||||
@ -997,6 +1004,8 @@ void CMapLoaderJson::readHeader(const bool complete)
|
|||||||
|
|
||||||
if(complete)
|
if(complete)
|
||||||
readOptions(handler);
|
readOptions(handler);
|
||||||
|
|
||||||
|
readTranslations();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMapLoaderJson::readTerrainTile(const std::string & src, TerrainTile & tile)
|
void CMapLoaderJson::readTerrainTile(const std::string & src, TerrainTile & tile)
|
||||||
@ -1122,12 +1131,12 @@ void CMapLoaderJson::readTerrainLevel(const JsonNode & src, const int index)
|
|||||||
void CMapLoaderJson::readTerrain()
|
void CMapLoaderJson::readTerrain()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
const JsonNode surface = getFromArchive("surface_terrain.json");
|
const JsonNode surface = getFromArchive(TERRAIN_FILE_NAMES[0]);
|
||||||
readTerrainLevel(surface, 0);
|
readTerrainLevel(surface, 0);
|
||||||
}
|
}
|
||||||
if(map->twoLevel)
|
if(map->twoLevel)
|
||||||
{
|
{
|
||||||
const JsonNode underground = getFromArchive("underground_terrain.json");
|
const JsonNode underground = getFromArchive(TERRAIN_FILE_NAMES[1]);
|
||||||
readTerrainLevel(underground, 1);
|
readTerrainLevel(underground, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1258,6 +1267,18 @@ void CMapLoaderJson::readObjects()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CMapLoaderJson::readTranslations()
|
||||||
|
{
|
||||||
|
std::list<Languages::Options> languages{Languages::getLanguageList().begin(), Languages::getLanguageList().end()};
|
||||||
|
for(auto & language : Languages::getLanguageList())
|
||||||
|
{
|
||||||
|
if(isExistArchive(language.identifier + ".json"))
|
||||||
|
mapHeader->translations.Struct()[language.identifier] = getFromArchive(language.identifier + ".json");
|
||||||
|
}
|
||||||
|
mapHeader->registerMapStrings();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
///CMapSaverJson
|
///CMapSaverJson
|
||||||
CMapSaverJson::CMapSaverJson(CInputOutputStream * stream)
|
CMapSaverJson::CMapSaverJson(CInputOutputStream * stream)
|
||||||
: buffer(stream)
|
: buffer(stream)
|
||||||
@ -1340,6 +1361,8 @@ void CMapSaverJson::writeHeader()
|
|||||||
|
|
||||||
writeOptions(handler);
|
writeOptions(handler);
|
||||||
|
|
||||||
|
writeTranslations();
|
||||||
|
|
||||||
addToArchive(header, HEADER_FILE_NAME);
|
addToArchive(header, HEADER_FILE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1388,12 +1411,12 @@ void CMapSaverJson::writeTerrain()
|
|||||||
//todo: multilevel map save support
|
//todo: multilevel map save support
|
||||||
|
|
||||||
JsonNode surface = writeTerrainLevel(0);
|
JsonNode surface = writeTerrainLevel(0);
|
||||||
addToArchive(surface, "surface_terrain.json");
|
addToArchive(surface, TERRAIN_FILE_NAMES[0]);
|
||||||
|
|
||||||
if(map->twoLevel)
|
if(map->twoLevel)
|
||||||
{
|
{
|
||||||
JsonNode underground = writeTerrainLevel(1);
|
JsonNode underground = writeTerrainLevel(1);
|
||||||
addToArchive(underground, "underground_terrain.json");
|
addToArchive(underground, TERRAIN_FILE_NAMES[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1439,5 +1462,19 @@ void CMapSaverJson::writeObjects()
|
|||||||
addToArchive(data, OBJECTS_FILE_NAME);
|
addToArchive(data, OBJECTS_FILE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CMapSaverJson::writeTranslations()
|
||||||
|
{
|
||||||
|
for(auto & s : mapHeader->translations.Struct())
|
||||||
|
{
|
||||||
|
auto & language = s.first;
|
||||||
|
if(Languages::getLanguageOptions(language).identifier.empty())
|
||||||
|
{
|
||||||
|
logGlobal->error("Serializing of unsupported language %s is not permitted", language);
|
||||||
|
continue;;
|
||||||
|
}
|
||||||
|
logGlobal->trace("Saving translations, language: %s", language);
|
||||||
|
addToArchive(s.second, language + ".json");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
@ -42,6 +42,7 @@ public:
|
|||||||
|
|
||||||
static const std::string HEADER_FILE_NAME;
|
static const std::string HEADER_FILE_NAME;
|
||||||
static const std::string OBJECTS_FILE_NAME;
|
static const std::string OBJECTS_FILE_NAME;
|
||||||
|
static const std::string TERRAIN_FILE_NAMES[2];
|
||||||
|
|
||||||
int fileVersionMajor;
|
int fileVersionMajor;
|
||||||
int fileVersionMinor;
|
int fileVersionMinor;
|
||||||
@ -201,6 +202,11 @@ public:
|
|||||||
* Reads complete map.
|
* Reads complete map.
|
||||||
*/
|
*/
|
||||||
void readMap();
|
void readMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads texts and translations
|
||||||
|
*/
|
||||||
|
void readTranslations();
|
||||||
|
|
||||||
static void readTerrainTile(const std::string & src, TerrainTile & tile);
|
static void readTerrainTile(const std::string & src, TerrainTile & tile);
|
||||||
|
|
||||||
@ -213,6 +219,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
void readObjects();
|
void readObjects();
|
||||||
|
|
||||||
|
bool isExistArchive(const std::string & archiveFilename);
|
||||||
JsonNode getFromArchive(const std::string & archiveFilename);
|
JsonNode getFromArchive(const std::string & archiveFilename);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -248,6 +255,11 @@ public:
|
|||||||
* Saves header to zip archive
|
* Saves header to zip archive
|
||||||
*/
|
*/
|
||||||
void writeHeader();
|
void writeHeader();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves texts and translations to zip archive
|
||||||
|
*/
|
||||||
|
void writeTranslations();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes one tile into string
|
* Encodes one tile into string
|
||||||
|
@ -319,8 +319,10 @@ TModID CModHandler::findResourceOrigin(const ResourcePath & name)
|
|||||||
|
|
||||||
std::string CModHandler::getModLanguage(const TModID& modId) const
|
std::string CModHandler::getModLanguage(const TModID& modId) const
|
||||||
{
|
{
|
||||||
if ( modId == "core")
|
if(modId == "core")
|
||||||
return VLC->generaltexth->getInstalledLanguage();
|
return VLC->generaltexth->getInstalledLanguage();
|
||||||
|
if(modId == "map")
|
||||||
|
return VLC->generaltexth->getPreferredLanguage();
|
||||||
return allMods.at(modId).baseLanguage;
|
return allMods.at(modId).baseLanguage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,8 +407,8 @@ void CMapGenerator::addHeaderInfo()
|
|||||||
m.width = mapGenOptions.getWidth();
|
m.width = mapGenOptions.getWidth();
|
||||||
m.height = mapGenOptions.getHeight();
|
m.height = mapGenOptions.getHeight();
|
||||||
m.twoLevel = mapGenOptions.getHasTwoLevels();
|
m.twoLevel = mapGenOptions.getHasTwoLevels();
|
||||||
m.name = VLC->generaltexth->allTexts[740];
|
m.name.appendLocalString(EMetaText::GENERAL_TXT, 740);
|
||||||
m.description = getMapDescription();
|
m.description.appendRawString(getMapDescription());
|
||||||
m.difficulty = 1;
|
m.difficulty = 1;
|
||||||
addPlayerInfo();
|
addPlayerInfo();
|
||||||
m.waterMap = (mapGenOptions.getWaterContent() != EWaterContent::EWaterContent::NONE);
|
m.waterMap = (mapGenOptions.getWaterContent() != EWaterContent::EWaterContent::NONE);
|
||||||
|
@ -111,18 +111,18 @@ void Object::Instance::setPositionRaw(const int3 & position)
|
|||||||
dObject.pos = dPosition + dParent.getPosition();
|
dObject.pos = dPosition + dParent.getPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Object::Instance::setAnyTemplate()
|
void Object::Instance::setAnyTemplate(CRandomGenerator & rng)
|
||||||
{
|
{
|
||||||
auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates();
|
auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates();
|
||||||
if(templates.empty())
|
if(templates.empty())
|
||||||
throw rmgException(boost::str(boost::format("Did not find any graphics for object (%d,%d)") % dObject.ID % dObject.subID));
|
throw rmgException(boost::str(boost::format("Did not find any graphics for object (%d,%d)") % dObject.ID % dObject.subID));
|
||||||
|
|
||||||
dObject.appearance = templates.front();
|
dObject.appearance = *RandomGeneratorUtil::nextItem(templates, rng);
|
||||||
dAccessibleAreaCache.clear();
|
dAccessibleAreaCache.clear();
|
||||||
setPosition(getPosition(false));
|
setPosition(getPosition(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Object::Instance::setTemplate(TerrainId terrain)
|
void Object::Instance::setTemplate(TerrainId terrain, CRandomGenerator & rng)
|
||||||
{
|
{
|
||||||
auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrain);
|
auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrain);
|
||||||
if (templates.empty())
|
if (templates.empty())
|
||||||
@ -130,7 +130,8 @@ void Object::Instance::setTemplate(TerrainId terrain)
|
|||||||
auto terrainName = VLC->terrainTypeHandler->getById(terrain)->getNameTranslated();
|
auto terrainName = VLC->terrainTypeHandler->getById(terrain)->getNameTranslated();
|
||||||
throw rmgException(boost::str(boost::format("Did not find graphics for object (%d,%d) at %s") % dObject.ID % dObject.subID % terrainName));
|
throw rmgException(boost::str(boost::format("Did not find graphics for object (%d,%d) at %s") % dObject.ID % dObject.subID % terrainName));
|
||||||
}
|
}
|
||||||
dObject.appearance = templates.front();
|
|
||||||
|
dObject.appearance = *RandomGeneratorUtil::nextItem(templates, rng);
|
||||||
dAccessibleAreaCache.clear();
|
dAccessibleAreaCache.clear();
|
||||||
setPosition(getPosition(false));
|
setPosition(getPosition(false));
|
||||||
}
|
}
|
||||||
@ -280,10 +281,10 @@ void Object::setPosition(const int3 & position)
|
|||||||
i.setPositionRaw(i.getPosition());
|
i.setPositionRaw(i.getPosition());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Object::setTemplate(const TerrainId & terrain)
|
void Object::setTemplate(const TerrainId & terrain, CRandomGenerator & rng)
|
||||||
{
|
{
|
||||||
for(auto& i : dInstances)
|
for(auto& i : dInstances)
|
||||||
i.setTemplate(terrain);
|
i.setTemplate(terrain, rng);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Area & Object::getArea() const
|
const Area & Object::getArea() const
|
||||||
@ -325,7 +326,7 @@ void rmg::Object::setGuardedIfMonster(const Instance& object)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Object::Instance::finalize(RmgMap & map)
|
void Object::Instance::finalize(RmgMap & map, CRandomGenerator & rng)
|
||||||
{
|
{
|
||||||
if(!map.isOnMap(getPosition(true)))
|
if(!map.isOnMap(getPosition(true)))
|
||||||
throw rmgException(boost::str(boost::format("Position of object %d at %s is outside the map") % dObject.id % getPosition(true).toString()));
|
throw rmgException(boost::str(boost::format("Position of object %d at %s is outside the map") % dObject.id % getPosition(true).toString()));
|
||||||
@ -341,7 +342,7 @@ void Object::Instance::finalize(RmgMap & map)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
setTemplate(terrainType->getId());
|
setTemplate(terrainType->getId(), rng);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,14 +363,14 @@ void Object::Instance::finalize(RmgMap & map)
|
|||||||
map.getMapProxy()->insertObject(&dObject);
|
map.getMapProxy()->insertObject(&dObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Object::finalize(RmgMap & map)
|
void Object::finalize(RmgMap & map, CRandomGenerator & rng)
|
||||||
{
|
{
|
||||||
if(dInstances.empty())
|
if(dInstances.empty())
|
||||||
throw rmgException("Cannot finalize object without instances");
|
throw rmgException("Cannot finalize object without instances");
|
||||||
|
|
||||||
for(auto & dInstance : dInstances)
|
for(auto & dInstance : dInstances)
|
||||||
{
|
{
|
||||||
dInstance.finalize(map);
|
dInstance.finalize(map, rng);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
class CGObjectInstance;
|
class CGObjectInstance;
|
||||||
|
class CRandomGenerator;
|
||||||
class RmgMap;
|
class RmgMap;
|
||||||
|
|
||||||
namespace rmg {
|
namespace rmg {
|
||||||
@ -35,8 +36,8 @@ public:
|
|||||||
int3 getVisitablePosition() const;
|
int3 getVisitablePosition() const;
|
||||||
bool isVisitableFrom(const int3 & tile) const;
|
bool isVisitableFrom(const int3 & tile) const;
|
||||||
const Area & getAccessibleArea() const;
|
const Area & getAccessibleArea() const;
|
||||||
void setTemplate(TerrainId terrain); //cache invalidation
|
void setTemplate(TerrainId terrain, CRandomGenerator &); //cache invalidation
|
||||||
void setAnyTemplate(); //cache invalidation
|
void setAnyTemplate(CRandomGenerator &); //cache invalidation
|
||||||
|
|
||||||
int3 getTopTile() const;
|
int3 getTopTile() const;
|
||||||
int3 getPosition(bool isAbsolute = false) const;
|
int3 getPosition(bool isAbsolute = false) const;
|
||||||
@ -45,7 +46,7 @@ public:
|
|||||||
const CGObjectInstance & object() const;
|
const CGObjectInstance & object() const;
|
||||||
CGObjectInstance & object();
|
CGObjectInstance & object();
|
||||||
|
|
||||||
void finalize(RmgMap & map); //cache invalidation
|
void finalize(RmgMap & map, CRandomGenerator &); //cache invalidation
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -73,7 +74,7 @@ public:
|
|||||||
|
|
||||||
const int3 & getPosition() const;
|
const int3 & getPosition() const;
|
||||||
void setPosition(const int3 & position);
|
void setPosition(const int3 & position);
|
||||||
void setTemplate(const TerrainId & terrain);
|
void setTemplate(const TerrainId & terrain, CRandomGenerator &);
|
||||||
|
|
||||||
const Area & getArea() const; //lazy cache invalidation
|
const Area & getArea() const; //lazy cache invalidation
|
||||||
const int3 getVisibleTop() const;
|
const int3 getVisibleTop() const;
|
||||||
@ -81,7 +82,7 @@ public:
|
|||||||
bool isGuarded() const;
|
bool isGuarded() const;
|
||||||
void setGuardedIfMonster(const Instance & object);
|
void setGuardedIfMonster(const Instance & object);
|
||||||
|
|
||||||
void finalize(RmgMap & map);
|
void finalize(RmgMap & map, CRandomGenerator &);
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -316,8 +316,8 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
|
|||||||
auto * gate2 = factory->create();
|
auto * gate2 = factory->create();
|
||||||
rmg::Object rmgGate1(*gate1);
|
rmg::Object rmgGate1(*gate1);
|
||||||
rmg::Object rmgGate2(*gate2);
|
rmg::Object rmgGate2(*gate2);
|
||||||
rmgGate1.setTemplate(zone.getTerrainType());
|
rmgGate1.setTemplate(zone.getTerrainType(), zone.getRand());
|
||||||
rmgGate2.setTemplate(otherZone->getTerrainType());
|
rmgGate2.setTemplate(otherZone->getTerrainType(), zone.getRand());
|
||||||
bool guarded1 = manager.addGuard(rmgGate1, connection.getGuardStrength(), true);
|
bool guarded1 = manager.addGuard(rmgGate1, connection.getGuardStrength(), true);
|
||||||
bool guarded2 = managerOther.addGuard(rmgGate2, connection.getGuardStrength(), true);
|
bool guarded2 = managerOther.addGuard(rmgGate2, connection.getGuardStrength(), true);
|
||||||
int minDist = 3;
|
int minDist = 3;
|
||||||
|
@ -79,25 +79,14 @@ void ObjectDistributor::distributeLimitedObjects()
|
|||||||
|
|
||||||
for (auto& zone : matchingZones)
|
for (auto& zone : matchingZones)
|
||||||
{
|
{
|
||||||
//We already know there are some templates
|
oi.generateObject = [primaryID, secondaryID]() -> CGObjectInstance *
|
||||||
auto templates = handler->getTemplates(zone->getTerrainType());
|
|
||||||
|
|
||||||
//FIXME: Templates empty?! Maybe zone changed terrain type over time?
|
|
||||||
|
|
||||||
//Assume the template with fewest terrains is the most suitable
|
|
||||||
auto temp = *boost::min_element(templates, [](std::shared_ptr<const ObjectTemplate> lhs, std::shared_ptr<const ObjectTemplate> rhs) -> bool
|
|
||||||
{
|
{
|
||||||
return lhs->getAllowedTerrains().size() < rhs->getAllowedTerrains().size();
|
return VLC->objtypeh->getHandlerFor(primaryID, secondaryID)->create();
|
||||||
});
|
|
||||||
|
|
||||||
oi.generateObject = [temp]() -> CGObjectInstance *
|
|
||||||
{
|
|
||||||
return VLC->objtypeh->getHandlerFor(temp->id, temp->subid)->create(temp);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
oi.value = rmgInfo.value;
|
oi.value = rmgInfo.value;
|
||||||
oi.probability = rmgInfo.rarity;
|
oi.probability = rmgInfo.rarity;
|
||||||
oi.templ = temp;
|
oi.setTemplates(primaryID, secondaryID, zone->getTerrainType());
|
||||||
|
|
||||||
//Rounding up will make sure all possible objects are exhausted
|
//Rounding up will make sure all possible objects are exhausted
|
||||||
uint32_t mapLimit = rmgInfo.mapLimit.value();
|
uint32_t mapLimit = rmgInfo.mapLimit.value();
|
||||||
@ -109,7 +98,7 @@ void ObjectDistributor::distributeLimitedObjects()
|
|||||||
|
|
||||||
rmgInfo.setMapLimit(mapLimit - oi.maxPerZone);
|
rmgInfo.setMapLimit(mapLimit - oi.maxPerZone);
|
||||||
//Don't add objects with 0 count remaining
|
//Don't add objects with 0 count remaining
|
||||||
if (oi.maxPerZone)
|
if(oi.maxPerZone && !oi.templates.empty())
|
||||||
{
|
{
|
||||||
zone->getModificator<TreasurePlacer>()->addObjectToRandomPool(oi);
|
zone->getModificator<TreasurePlacer>()->addObjectToRandomPool(oi);
|
||||||
}
|
}
|
||||||
|
@ -354,7 +354,7 @@ bool ObjectManager::createRequiredObjects()
|
|||||||
for(const auto & objInfo : requiredObjects)
|
for(const auto & objInfo : requiredObjects)
|
||||||
{
|
{
|
||||||
rmg::Object rmgObject(*objInfo.obj);
|
rmg::Object rmgObject(*objInfo.obj);
|
||||||
rmgObject.setTemplate(zone.getTerrainType());
|
rmgObject.setTemplate(zone.getTerrainType(), zone.getRand());
|
||||||
bool guarded = addGuard(rmgObject, objInfo.guardStrength, (objInfo.obj->ID == Obj::MONOLITH_TWO_WAY));
|
bool guarded = addGuard(rmgObject, objInfo.guardStrength, (objInfo.obj->ID == Obj::MONOLITH_TWO_WAY));
|
||||||
|
|
||||||
Zone::Lock lock(zone.areaMutex);
|
Zone::Lock lock(zone.areaMutex);
|
||||||
@ -394,7 +394,7 @@ bool ObjectManager::createRequiredObjects()
|
|||||||
auto possibleArea = zone.areaPossible();
|
auto possibleArea = zone.areaPossible();
|
||||||
|
|
||||||
rmg::Object rmgObject(*objInfo.obj);
|
rmg::Object rmgObject(*objInfo.obj);
|
||||||
rmgObject.setTemplate(zone.getTerrainType());
|
rmgObject.setTemplate(zone.getTerrainType(), zone.getRand());
|
||||||
bool guarded = addGuard(rmgObject, objInfo.guardStrength, (objInfo.obj->ID == Obj::MONOLITH_TWO_WAY));
|
bool guarded = addGuard(rmgObject, objInfo.guardStrength, (objInfo.obj->ID == Obj::MONOLITH_TWO_WAY));
|
||||||
auto path = placeAndConnectObject(zone.areaPossible(), rmgObject,
|
auto path = placeAndConnectObject(zone.areaPossible(), rmgObject,
|
||||||
[this, &rmgObject](const int3 & tile)
|
[this, &rmgObject](const int3 & tile)
|
||||||
@ -480,7 +480,7 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
|
|||||||
if (!monster->object().appearance)
|
if (!monster->object().appearance)
|
||||||
{
|
{
|
||||||
//Needed to determine visitable offset
|
//Needed to determine visitable offset
|
||||||
monster->setAnyTemplate();
|
monster->setAnyTemplate(zone.getRand());
|
||||||
}
|
}
|
||||||
object.getPosition();
|
object.getPosition();
|
||||||
auto visitableOffset = monster->object().getVisitableOffset();
|
auto visitableOffset = monster->object().getVisitableOffset();
|
||||||
@ -492,7 +492,7 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
|
|||||||
int3 parentOffset = monster->getPosition(true) - monster->getPosition(false);
|
int3 parentOffset = monster->getPosition(true) - monster->getPosition(false);
|
||||||
monster->setPosition(fixedPos - parentOffset);
|
monster->setPosition(fixedPos - parentOffset);
|
||||||
}
|
}
|
||||||
object.finalize(map);
|
object.finalize(map, zone.getRand());
|
||||||
|
|
||||||
Zone::Lock lock(zone.areaMutex);
|
Zone::Lock lock(zone.areaMutex);
|
||||||
zone.areaPossible().subtract(object.getArea());
|
zone.areaPossible().subtract(object.getArea());
|
||||||
@ -689,7 +689,7 @@ bool ObjectManager::addGuard(rmg::Object & object, si32 strength, bool zoneGuard
|
|||||||
});
|
});
|
||||||
|
|
||||||
auto & instance = object.addInstance(*guard);
|
auto & instance = object.addInstance(*guard);
|
||||||
instance.setAnyTemplate(); //terrain is irrelevant for monsters, but monsters need some template now
|
instance.setAnyTemplate(zone.getRand()); //terrain is irrelevant for monsters, but monsters need some template now
|
||||||
|
|
||||||
//Fix HoTA monsters with offset template
|
//Fix HoTA monsters with offset template
|
||||||
auto visitableOffset = instance.object().getVisitableOffset();
|
auto visitableOffset = instance.object().getVisitableOffset();
|
||||||
|
@ -397,7 +397,7 @@ void RiverPlacer::connectRiver(const int3 & tile)
|
|||||||
{
|
{
|
||||||
auto * obj = handler->create(templ);
|
auto * obj = handler->create(templ);
|
||||||
rmg::Object deltaObj(*obj, deltaPositions[pos]);
|
rmg::Object deltaObj(*obj, deltaPositions[pos]);
|
||||||
deltaObj.finalize(map);
|
deltaObj.finalize(map, zone.getRand());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ int3 TownPlacer::placeMainTown(ObjectManager & manager, CGTownInstance & town)
|
|||||||
{
|
{
|
||||||
//towns are big objects and should be centered around visitable position
|
//towns are big objects and should be centered around visitable position
|
||||||
rmg::Object rmgObject(town);
|
rmg::Object rmgObject(town);
|
||||||
rmgObject.setTemplate(zone.getTerrainType());
|
rmgObject.setTemplate(zone.getTerrainType(), zone.getRand());
|
||||||
|
|
||||||
int3 position(-1, -1, -1);
|
int3 position(-1, -1, -1);
|
||||||
{
|
{
|
||||||
|
@ -72,26 +72,16 @@ void TreasurePlacer::addAllPossibleObjects()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto templates = handler->getTemplates(zone.getTerrainType());
|
oi.generateObject = [primaryID, secondaryID]() -> CGObjectInstance *
|
||||||
if (templates.empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
//TODO: Reuse chooseRandomAppearance (eg. WoG treasure chests)
|
|
||||||
//Assume the template with fewest terrains is the most suitable
|
|
||||||
auto temp = *boost::min_element(templates, [](std::shared_ptr<const ObjectTemplate> lhs, std::shared_ptr<const ObjectTemplate> rhs) -> bool
|
|
||||||
{
|
{
|
||||||
return lhs->getAllowedTerrains().size() < rhs->getAllowedTerrains().size();
|
return VLC->objtypeh->getHandlerFor(primaryID, secondaryID)->create();
|
||||||
});
|
|
||||||
|
|
||||||
oi.generateObject = [temp]() -> CGObjectInstance *
|
|
||||||
{
|
|
||||||
return VLC->objtypeh->getHandlerFor(temp->id, temp->subid)->create(temp);
|
|
||||||
};
|
};
|
||||||
oi.value = rmgInfo.value;
|
oi.value = rmgInfo.value;
|
||||||
oi.probability = rmgInfo.rarity;
|
oi.probability = rmgInfo.rarity;
|
||||||
oi.templ = temp;
|
oi.setTemplates(primaryID, secondaryID, zone.getTerrainType());
|
||||||
oi.maxPerZone = rmgInfo.zoneLimit;
|
oi.maxPerZone = rmgInfo.zoneLimit;
|
||||||
addObjectToRandomPool(oi);
|
if(!oi.templates.empty())
|
||||||
|
addObjectToRandomPool(oi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,18 +115,18 @@ void TreasurePlacer::addAllPossibleObjects()
|
|||||||
obj->exp = generator.getConfig().prisonExperience[i];
|
obj->exp = generator.getConfig().prisonExperience[i];
|
||||||
obj->setOwner(PlayerColor::NEUTRAL);
|
obj->setOwner(PlayerColor::NEUTRAL);
|
||||||
generator.banHero(hid);
|
generator.banHero(hid);
|
||||||
obj->appearance = VLC->objtypeh->getHandlerFor(Obj::PRISON, 0)->getTemplates(zone.getTerrainType()).front(); //can't init template with hero subID
|
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
oi.setTemplate(Obj::PRISON, 0, zone.getTerrainType());
|
oi.setTemplates(Obj::PRISON, 0, zone.getTerrainType());
|
||||||
oi.value = generator.getConfig().prisonValues[i];
|
oi.value = generator.getConfig().prisonValues[i];
|
||||||
oi.probability = 30;
|
oi.probability = 30;
|
||||||
|
|
||||||
//Distribute all allowed prisons, starting from the most valuable
|
//Distribute all allowed prisons, starting from the most valuable
|
||||||
oi.maxPerZone = (std::ceil((float)prisonsLeft / (i + 1)));
|
oi.maxPerZone = (std::ceil((float)prisonsLeft / (i + 1)));
|
||||||
prisonsLeft -= oi.maxPerZone;
|
prisonsLeft -= oi.maxPerZone;
|
||||||
addObjectToRandomPool(oi);
|
if(!oi.templates.empty())
|
||||||
|
addObjectToRandomPool(oi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,22 +173,16 @@ void TreasurePlacer::addAllPossibleObjects()
|
|||||||
auto nativeZonesCount = static_cast<float>(map.getZoneCount(cre->getFaction()));
|
auto nativeZonesCount = static_cast<float>(map.getZoneCount(cre->getFaction()));
|
||||||
oi.value = static_cast<ui32>(cre->getAIValue() * cre->getGrowth() * (1 + (nativeZonesCount / map.getTotalZoneCount()) + (nativeZonesCount / 2)));
|
oi.value = static_cast<ui32>(cre->getAIValue() * cre->getGrowth() * (1 + (nativeZonesCount / map.getTotalZoneCount()) + (nativeZonesCount / 2)));
|
||||||
oi.probability = 40;
|
oi.probability = 40;
|
||||||
|
|
||||||
for(const auto & tmplate : dwellingHandler->getTemplates())
|
oi.generateObject = [secondaryID, dwellingType]() -> CGObjectInstance *
|
||||||
{
|
{
|
||||||
if(tmplate->canBePlacedAt(zone.getTerrainType()))
|
auto * obj = VLC->objtypeh->getHandlerFor(dwellingType, secondaryID)->create();
|
||||||
{
|
obj->tempOwner = PlayerColor::NEUTRAL;
|
||||||
oi.generateObject = [tmplate, secondaryID, dwellingType]() -> CGObjectInstance *
|
return obj;
|
||||||
{
|
};
|
||||||
auto * obj = VLC->objtypeh->getHandlerFor(dwellingType, secondaryID)->create(tmplate);
|
oi.setTemplates(dwellingType, secondaryID, zone.getTerrainType());
|
||||||
obj->tempOwner = PlayerColor::NEUTRAL;
|
if(!oi.templates.empty())
|
||||||
return obj;
|
addObjectToRandomPool(oi);
|
||||||
};
|
|
||||||
|
|
||||||
oi.templ = tmplate;
|
|
||||||
addObjectToRandomPool(oi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -222,10 +206,11 @@ void TreasurePlacer::addAllPossibleObjects()
|
|||||||
obj->storedArtifact = a;
|
obj->storedArtifact = a;
|
||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
oi.setTemplate(Obj::SPELL_SCROLL, 0, zone.getTerrainType());
|
oi.setTemplates(Obj::SPELL_SCROLL, 0, zone.getTerrainType());
|
||||||
oi.value = generator.getConfig().scrollValues[i];
|
oi.value = generator.getConfig().scrollValues[i];
|
||||||
oi.probability = 30;
|
oi.probability = 30;
|
||||||
addObjectToRandomPool(oi);
|
if(!oi.templates.empty())
|
||||||
|
addObjectToRandomPool(oi);
|
||||||
}
|
}
|
||||||
|
|
||||||
//pandora box with gold
|
//pandora box with gold
|
||||||
@ -243,10 +228,11 @@ void TreasurePlacer::addAllPossibleObjects()
|
|||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
|
oi.setTemplates(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
|
||||||
oi.value = i * generator.getConfig().pandoraMultiplierGold;
|
oi.value = i * generator.getConfig().pandoraMultiplierGold;
|
||||||
oi.probability = 5;
|
oi.probability = 5;
|
||||||
addObjectToRandomPool(oi);
|
if(!oi.templates.empty())
|
||||||
|
addObjectToRandomPool(oi);
|
||||||
}
|
}
|
||||||
|
|
||||||
//pandora box with experience
|
//pandora box with experience
|
||||||
@ -264,10 +250,11 @@ void TreasurePlacer::addAllPossibleObjects()
|
|||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
|
oi.setTemplates(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
|
||||||
oi.value = i * generator.getConfig().pandoraMultiplierExperience;
|
oi.value = i * generator.getConfig().pandoraMultiplierExperience;
|
||||||
oi.probability = 20;
|
oi.probability = 20;
|
||||||
addObjectToRandomPool(oi);
|
if(!oi.templates.empty())
|
||||||
|
addObjectToRandomPool(oi);
|
||||||
}
|
}
|
||||||
|
|
||||||
//pandora box with creatures
|
//pandora box with creatures
|
||||||
@ -325,10 +312,11 @@ void TreasurePlacer::addAllPossibleObjects()
|
|||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
|
oi.setTemplates(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
|
||||||
oi.value = static_cast<ui32>((2 * (creature->getAIValue()) * creaturesAmount * (1 + static_cast<float>(map.getZoneCount(creature->getFaction())) / map.getTotalZoneCount())) / 3);
|
oi.value = static_cast<ui32>((2 * (creature->getAIValue()) * creaturesAmount * (1 + static_cast<float>(map.getZoneCount(creature->getFaction())) / map.getTotalZoneCount())) / 3);
|
||||||
oi.probability = 3;
|
oi.probability = 3;
|
||||||
addObjectToRandomPool(oi);
|
if(!oi.templates.empty())
|
||||||
|
addObjectToRandomPool(oi);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Pandora with 12 spells of certain level
|
//Pandora with 12 spells of certain level
|
||||||
@ -357,10 +345,11 @@ void TreasurePlacer::addAllPossibleObjects()
|
|||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
|
oi.setTemplates(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
|
||||||
oi.value = (i + 1) * generator.getConfig().pandoraMultiplierSpells; //5000 - 15000
|
oi.value = (i + 1) * generator.getConfig().pandoraMultiplierSpells; //5000 - 15000
|
||||||
oi.probability = 2;
|
oi.probability = 2;
|
||||||
addObjectToRandomPool(oi);
|
if(!oi.templates.empty())
|
||||||
|
addObjectToRandomPool(oi);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Pandora with 15 spells of certain school
|
//Pandora with 15 spells of certain school
|
||||||
@ -389,10 +378,11 @@ void TreasurePlacer::addAllPossibleObjects()
|
|||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
|
oi.setTemplates(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
|
||||||
oi.value = generator.getConfig().pandoraSpellSchool;
|
oi.value = generator.getConfig().pandoraSpellSchool;
|
||||||
oi.probability = 2;
|
oi.probability = 2;
|
||||||
addObjectToRandomPool(oi);
|
if(!oi.templates.empty())
|
||||||
|
addObjectToRandomPool(oi);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pandora box with 60 random spells
|
// Pandora box with 60 random spells
|
||||||
@ -420,10 +410,11 @@ void TreasurePlacer::addAllPossibleObjects()
|
|||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
|
oi.setTemplates(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
|
||||||
oi.value = generator.getConfig().pandoraSpell60;
|
oi.value = generator.getConfig().pandoraSpell60;
|
||||||
oi.probability = 2;
|
oi.probability = 2;
|
||||||
addObjectToRandomPool(oi);
|
if(!oi.templates.empty())
|
||||||
|
addObjectToRandomPool(oi);
|
||||||
|
|
||||||
//Seer huts with creatures or generic rewards
|
//Seer huts with creatures or generic rewards
|
||||||
|
|
||||||
@ -483,7 +474,7 @@ void TreasurePlacer::addAllPossibleObjects()
|
|||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
oi.probability = 3;
|
oi.probability = 3;
|
||||||
oi.setTemplate(Obj::SEER_HUT, randomAppearance, zone.getTerrainType());
|
oi.setTemplates(Obj::SEER_HUT, randomAppearance, zone.getTerrainType());
|
||||||
oi.value = static_cast<ui32>(((2 * (creature->getAIValue()) * creaturesAmount * (1 + static_cast<float>(map.getZoneCount(creature->getFaction())) / map.getTotalZoneCount())) - 4000) / 3);
|
oi.value = static_cast<ui32>(((2 * (creature->getAIValue()) * creaturesAmount * (1 + static_cast<float>(map.getZoneCount(creature->getFaction())) / map.getTotalZoneCount())) - 4000) / 3);
|
||||||
if (oi.value > zone.getMaxTreasureValue())
|
if (oi.value > zone.getMaxTreasureValue())
|
||||||
{
|
{
|
||||||
@ -491,7 +482,8 @@ void TreasurePlacer::addAllPossibleObjects()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
possibleSeerHuts.push_back(oi);
|
if(!oi.templates.empty())
|
||||||
|
possibleSeerHuts.push_back(oi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -500,7 +492,7 @@ void TreasurePlacer::addAllPossibleObjects()
|
|||||||
{
|
{
|
||||||
int randomAppearance = chooseRandomAppearance(zone.getRand(), Obj::SEER_HUT, zone.getTerrainType());
|
int randomAppearance = chooseRandomAppearance(zone.getRand(), Obj::SEER_HUT, zone.getTerrainType());
|
||||||
|
|
||||||
oi.setTemplate(Obj::SEER_HUT, randomAppearance, zone.getTerrainType());
|
oi.setTemplates(Obj::SEER_HUT, randomAppearance, zone.getTerrainType());
|
||||||
oi.value = generator.getConfig().questValues[i];
|
oi.value = generator.getConfig().questValues[i];
|
||||||
if (oi.value > zone.getMaxTreasureValue())
|
if (oi.value > zone.getMaxTreasureValue())
|
||||||
{
|
{
|
||||||
@ -533,7 +525,8 @@ void TreasurePlacer::addAllPossibleObjects()
|
|||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
|
|
||||||
possibleSeerHuts.push_back(oi);
|
if(!oi.templates.empty())
|
||||||
|
possibleSeerHuts.push_back(oi);
|
||||||
|
|
||||||
oi.generateObject = [i, randomAppearance, this, qap]() -> CGObjectInstance *
|
oi.generateObject = [i, randomAppearance, this, qap]() -> CGObjectInstance *
|
||||||
{
|
{
|
||||||
@ -557,7 +550,8 @@ void TreasurePlacer::addAllPossibleObjects()
|
|||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
|
|
||||||
possibleSeerHuts.push_back(oi);
|
if(!oi.templates.empty())
|
||||||
|
possibleSeerHuts.push_back(oi);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (possibleSeerHuts.empty())
|
if (possibleSeerHuts.empty())
|
||||||
@ -610,7 +604,12 @@ std::vector<ObjectInfo*> TreasurePlacer::prepareTreasurePile(const CTreasureInfo
|
|||||||
if(!oi) //fail
|
if(!oi) //fail
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if(oi->templ->isVisitableFromTop())
|
bool visitableFromTop = true;
|
||||||
|
for(auto & t : oi->templates)
|
||||||
|
if(!t->isVisitableFromTop())
|
||||||
|
visitableFromTop = false;
|
||||||
|
|
||||||
|
if(visitableFromTop)
|
||||||
{
|
{
|
||||||
objectInfos.push_back(oi);
|
objectInfos.push_back(oi);
|
||||||
}
|
}
|
||||||
@ -641,7 +640,10 @@ rmg::Object TreasurePlacer::constructTreasurePile(const std::vector<ObjectInfo*>
|
|||||||
accessibleArea.add(int3());
|
accessibleArea.add(int3());
|
||||||
|
|
||||||
auto * object = oi->generateObject();
|
auto * object = oi->generateObject();
|
||||||
object->appearance = oi->templ;
|
if(oi->templates.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
object->appearance = *RandomGeneratorUtil::nextItem(oi->templates, zone.getRand());
|
||||||
auto & instance = rmgObject.addInstance(*object);
|
auto & instance = rmgObject.addInstance(*object);
|
||||||
|
|
||||||
do
|
do
|
||||||
@ -717,7 +719,12 @@ ObjectInfo * TreasurePlacer::getRandomObject(ui32 desiredValue, ui32 currentValu
|
|||||||
if(oi.value > maxVal)
|
if(oi.value > maxVal)
|
||||||
break; //this assumes values are sorted in ascending order
|
break; //this assumes values are sorted in ascending order
|
||||||
|
|
||||||
if(!oi.templ->isVisitableFromTop() && !allowLargeObjects)
|
bool visitableFromTop = true;
|
||||||
|
for(auto & t : oi.templates)
|
||||||
|
if(!t->isVisitableFromTop())
|
||||||
|
visitableFromTop = false;
|
||||||
|
|
||||||
|
if(!visitableFromTop && !allowLargeObjects)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if(oi.value >= minValue && oi.maxPerZone > 0)
|
if(oi.value >= minValue && oi.maxPerZone > 0)
|
||||||
@ -921,17 +928,13 @@ char TreasurePlacer::dump(const int3 & t)
|
|||||||
return Modificator::dump(t);
|
return Modificator::dump(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectInfo::setTemplate(si32 type, si32 subtype, TerrainId terrainType)
|
void ObjectInfo::setTemplates(si32 type, si32 subtype, TerrainId terrainType)
|
||||||
{
|
{
|
||||||
auto templHandler = VLC->objtypeh->getHandlerFor(type, subtype);
|
auto templHandler = VLC->objtypeh->getHandlerFor(type, subtype);
|
||||||
if(!templHandler)
|
if(!templHandler)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto templates = templHandler->getTemplates(terrainType);
|
templates = templHandler->getTemplates(terrainType);
|
||||||
if(templates.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
templ = templates.front();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
@ -22,16 +22,14 @@ class CRandomGenerator;
|
|||||||
|
|
||||||
struct ObjectInfo
|
struct ObjectInfo
|
||||||
{
|
{
|
||||||
std::shared_ptr<const ObjectTemplate> templ;
|
std::vector<std::shared_ptr<const ObjectTemplate>> templates;
|
||||||
ui32 value = 0;
|
ui32 value = 0;
|
||||||
ui16 probability = 0;
|
ui16 probability = 0;
|
||||||
ui32 maxPerZone = 1;
|
ui32 maxPerZone = 1;
|
||||||
//ui32 maxPerMap; //unused
|
//ui32 maxPerMap; //unused
|
||||||
std::function<CGObjectInstance *()> generateObject;
|
std::function<CGObjectInstance *()> generateObject;
|
||||||
|
|
||||||
void setTemplate(si32 type, si32 subtype, TerrainId terrain);
|
void setTemplates(si32 type, si32 subtype, TerrainId terrain);
|
||||||
|
|
||||||
bool operator==(const ObjectInfo& oi) const { return (templ == oi.templ); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class TreasurePlacer: public Modificator
|
class TreasurePlacer: public Modificator
|
||||||
|
@ -254,7 +254,7 @@ bool WaterProxy::placeBoat(Zone & land, const Lake & lake, bool createRoad, Rout
|
|||||||
auto * boat = dynamic_cast<CGBoat *>(VLC->objtypeh->getHandlerFor(Obj::BOAT, *RandomGeneratorUtil::nextItem(sailingBoatTypes, zone.getRand()))->create());
|
auto * boat = dynamic_cast<CGBoat *>(VLC->objtypeh->getHandlerFor(Obj::BOAT, *RandomGeneratorUtil::nextItem(sailingBoatTypes, zone.getRand()))->create());
|
||||||
|
|
||||||
rmg::Object rmgObject(*boat);
|
rmg::Object rmgObject(*boat);
|
||||||
rmgObject.setTemplate(zone.getTerrainType());
|
rmgObject.setTemplate(zone.getTerrainType(), zone.getRand());
|
||||||
|
|
||||||
auto waterAvailable = zone.areaPossible() + zone.freePaths();
|
auto waterAvailable = zone.areaPossible() + zone.freePaths();
|
||||||
rmg::Area coast = lake.neighbourZones.at(land.getId()); //having land tiles
|
rmg::Area coast = lake.neighbourZones.at(land.getId()); //having land tiles
|
||||||
@ -319,7 +319,7 @@ bool WaterProxy::placeShipyard(Zone & land, const Lake & lake, si32 guard, bool
|
|||||||
shipyard->tempOwner = PlayerColor::NEUTRAL;
|
shipyard->tempOwner = PlayerColor::NEUTRAL;
|
||||||
|
|
||||||
rmg::Object rmgObject(*shipyard);
|
rmg::Object rmgObject(*shipyard);
|
||||||
rmgObject.setTemplate(land.getTerrainType());
|
rmgObject.setTemplate(land.getTerrainType(), zone.getRand());
|
||||||
bool guarded = manager->addGuard(rmgObject, guard);
|
bool guarded = manager->addGuard(rmgObject, guard);
|
||||||
|
|
||||||
auto waterAvailable = zone.areaPossible() + zone.freePaths();
|
auto waterAvailable = zone.areaPossible() + zone.freePaths();
|
||||||
|
@ -21,6 +21,7 @@ set(editor_SRCS
|
|||||||
mapsettings/loseconditions.cpp
|
mapsettings/loseconditions.cpp
|
||||||
mapsettings/eventsettings.cpp
|
mapsettings/eventsettings.cpp
|
||||||
mapsettings/rumorsettings.cpp
|
mapsettings/rumorsettings.cpp
|
||||||
|
mapsettings/translations.cpp
|
||||||
playersettings.cpp
|
playersettings.cpp
|
||||||
playerparams.cpp
|
playerparams.cpp
|
||||||
scenelayer.cpp
|
scenelayer.cpp
|
||||||
@ -58,6 +59,7 @@ set(editor_HEADERS
|
|||||||
mapsettings/loseconditions.h
|
mapsettings/loseconditions.h
|
||||||
mapsettings/eventsettings.h
|
mapsettings/eventsettings.h
|
||||||
mapsettings/rumorsettings.h
|
mapsettings/rumorsettings.h
|
||||||
|
mapsettings/translations.h
|
||||||
playersettings.h
|
playersettings.h
|
||||||
playerparams.h
|
playerparams.h
|
||||||
scenelayer.h
|
scenelayer.h
|
||||||
@ -85,6 +87,7 @@ set(editor_FORMS
|
|||||||
mapsettings/loseconditions.ui
|
mapsettings/loseconditions.ui
|
||||||
mapsettings/eventsettings.ui
|
mapsettings/eventsettings.ui
|
||||||
mapsettings/rumorsettings.ui
|
mapsettings/rumorsettings.ui
|
||||||
|
mapsettings/translations.ui
|
||||||
playersettings.ui
|
playersettings.ui
|
||||||
playerparams.ui
|
playerparams.ui
|
||||||
validator.ui
|
validator.ui
|
||||||
|
BIN
mapeditor/icons/translations.png
Normal file
BIN
mapeditor/icons/translations.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
@ -277,8 +277,8 @@ void Inspector::updateProperties(CGHeroInstance * o)
|
|||||||
delegate->options = {{"MALE", QVariant::fromValue(int(EHeroGender::MALE))}, {"FEMALE", QVariant::fromValue(int(EHeroGender::FEMALE))}};
|
delegate->options = {{"MALE", QVariant::fromValue(int(EHeroGender::MALE))}, {"FEMALE", QVariant::fromValue(int(EHeroGender::FEMALE))}};
|
||||||
addProperty<std::string>("Gender", (o->gender == EHeroGender::FEMALE ? "FEMALE" : "MALE"), delegate , false);
|
addProperty<std::string>("Gender", (o->gender == EHeroGender::FEMALE ? "FEMALE" : "MALE"), delegate , false);
|
||||||
}
|
}
|
||||||
addProperty("Name", o->nameCustom, false);
|
addProperty("Name", o->getNameTranslated(), false);
|
||||||
addProperty("Biography", o->biographyCustom, new MessageDelegate, false);
|
addProperty("Biography", o->getBiographyTranslated(), new MessageDelegate, false);
|
||||||
addProperty("Portrait", o->portrait, false);
|
addProperty("Portrait", o->portrait, false);
|
||||||
|
|
||||||
auto * delegate = new HeroSkillsDelegate(*o);
|
auto * delegate = new HeroSkillsDelegate(*o);
|
||||||
@ -531,7 +531,7 @@ void Inspector::setProperty(CGPandoraBox * o, const QString & key, const QVarian
|
|||||||
if(!o) return;
|
if(!o) return;
|
||||||
|
|
||||||
if(key == "Message")
|
if(key == "Message")
|
||||||
o->message = value.toString().toStdString();
|
o->message = MetaString::createFromTextID(mapRegisterLocalizedString("map", *map, TextIdentifier("guards", o->instanceName, "message"), value.toString().toStdString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Inspector::setProperty(CGEvent * o, const QString & key, const QVariant & value)
|
void Inspector::setProperty(CGEvent * o, const QString & key, const QVariant & value)
|
||||||
@ -553,7 +553,7 @@ void Inspector::setProperty(CGTownInstance * o, const QString & key, const QVari
|
|||||||
if(!o) return;
|
if(!o) return;
|
||||||
|
|
||||||
if(key == "Town name")
|
if(key == "Town name")
|
||||||
o->setNameTranslated(value.toString().toStdString());
|
o->setNameTextId(mapRegisterLocalizedString("map", *map, TextIdentifier("town", o->instanceName, "name"), value.toString().toStdString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Inspector::setProperty(CGSignBottle * o, const QString & key, const QVariant & value)
|
void Inspector::setProperty(CGSignBottle * o, const QString & key, const QVariant & value)
|
||||||
@ -561,7 +561,7 @@ void Inspector::setProperty(CGSignBottle * o, const QString & key, const QVarian
|
|||||||
if(!o) return;
|
if(!o) return;
|
||||||
|
|
||||||
if(key == "Message")
|
if(key == "Message")
|
||||||
o->message = value.toString().toStdString();
|
o->message = MetaString::createFromTextID(mapRegisterLocalizedString("map", *map, TextIdentifier("sign", o->instanceName, "message"), value.toString().toStdString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Inspector::setProperty(CGMine * o, const QString & key, const QVariant & value)
|
void Inspector::setProperty(CGMine * o, const QString & key, const QVariant & value)
|
||||||
@ -577,7 +577,7 @@ void Inspector::setProperty(CGArtifact * o, const QString & key, const QVariant
|
|||||||
if(!o) return;
|
if(!o) return;
|
||||||
|
|
||||||
if(key == "Message")
|
if(key == "Message")
|
||||||
o->message = value.toString().toStdString();
|
o->message = MetaString::createFromTextID(mapRegisterLocalizedString("map", *map, TextIdentifier("guards", o->instanceName, "message"), value.toString().toStdString()));
|
||||||
|
|
||||||
if(o->storedArtifact && key == "Spell")
|
if(o->storedArtifact && key == "Spell")
|
||||||
{
|
{
|
||||||
@ -606,7 +606,10 @@ void Inspector::setProperty(CGHeroInstance * o, const QString & key, const QVari
|
|||||||
o->gender = EHeroGender(value.toInt());
|
o->gender = EHeroGender(value.toInt());
|
||||||
|
|
||||||
if(key == "Name")
|
if(key == "Name")
|
||||||
o->nameCustom = value.toString().toStdString();
|
o->nameCustomTextId = mapRegisterLocalizedString("map", *map, TextIdentifier("hero", o->instanceName, "name"), value.toString().toStdString());
|
||||||
|
|
||||||
|
if(key == "Biography")
|
||||||
|
o->biographyCustomTextId = mapRegisterLocalizedString("map", *map, TextIdentifier("hero", o->instanceName, "biography"), value.toString().toStdString());
|
||||||
|
|
||||||
if(key == "Experience")
|
if(key == "Experience")
|
||||||
o->exp = value.toString().toInt();
|
o->exp = value.toString().toInt();
|
||||||
@ -643,7 +646,7 @@ void Inspector::setProperty(CGCreature * o, const QString & key, const QVariant
|
|||||||
if(!o) return;
|
if(!o) return;
|
||||||
|
|
||||||
if(key == "Message")
|
if(key == "Message")
|
||||||
o->message = value.toString().toStdString();
|
o->message = MetaString::createFromTextID(mapRegisterLocalizedString("map", *map, TextIdentifier("monster", o->instanceName, "message"), value.toString().toStdString()));
|
||||||
if(key == "Character")
|
if(key == "Character")
|
||||||
o->character = CGCreature::Character(value.toInt());
|
o->character = CGCreature::Character(value.toInt());
|
||||||
if(key == "Never flees")
|
if(key == "Never flees")
|
||||||
@ -661,11 +664,11 @@ void Inspector::setProperty(CGSeerHut * o, const QString & key, const QVariant &
|
|||||||
if(key == "Mission type")
|
if(key == "Mission type")
|
||||||
o->quest->missionType = CQuest::Emission(value.toInt());
|
o->quest->missionType = CQuest::Emission(value.toInt());
|
||||||
if(key == "First visit text")
|
if(key == "First visit text")
|
||||||
o->quest->firstVisitText = value.toString().toStdString();
|
o->quest->firstVisitText = MetaString::createFromTextID(mapRegisterLocalizedString("map", *map, TextIdentifier("quest", o->instanceName, "firstVisit"), value.toString().toStdString()));
|
||||||
if(key == "Next visit text")
|
if(key == "Next visit text")
|
||||||
o->quest->nextVisitText = value.toString().toStdString();
|
o->quest->nextVisitText = MetaString::createFromTextID(mapRegisterLocalizedString("map", *map, TextIdentifier("quest", o->instanceName, "nextVisit"), value.toString().toStdString()));
|
||||||
if(key == "Completed text")
|
if(key == "Completed text")
|
||||||
o->quest->completedText = value.toString().toStdString();
|
o->quest->completedText = MetaString::createFromTextID(mapRegisterLocalizedString("map", *map, TextIdentifier("quest", o->instanceName, "completed"), value.toString().toStdString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -713,6 +716,16 @@ QTableWidgetItem * Inspector::addProperty(const std::string & value)
|
|||||||
return addProperty(QString::fromStdString(value));
|
return addProperty(QString::fromStdString(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QTableWidgetItem * Inspector::addProperty(const TextIdentifier & value)
|
||||||
|
{
|
||||||
|
return addProperty(VLC->generaltexth->translate(value.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
QTableWidgetItem * Inspector::addProperty(const MetaString & value)
|
||||||
|
{
|
||||||
|
return addProperty(value.toString());
|
||||||
|
}
|
||||||
|
|
||||||
QTableWidgetItem * Inspector::addProperty(const QString & value)
|
QTableWidgetItem * Inspector::addProperty(const QString & value)
|
||||||
{
|
{
|
||||||
auto * item = new QTableWidgetItem(value);
|
auto * item = new QTableWidgetItem(value);
|
||||||
|
@ -18,7 +18,9 @@
|
|||||||
#include "../lib/mapObjects/CGCreature.h"
|
#include "../lib/mapObjects/CGCreature.h"
|
||||||
#include "../lib/mapObjects/MapObjects.h"
|
#include "../lib/mapObjects/MapObjects.h"
|
||||||
#include "../lib/mapObjects/CRewardableObject.h"
|
#include "../lib/mapObjects/CRewardableObject.h"
|
||||||
|
#include "../lib/CGeneralTextHandler.h"
|
||||||
#include "../lib/ResourceSet.h"
|
#include "../lib/ResourceSet.h"
|
||||||
|
#include "../lib/MetaString.h"
|
||||||
|
|
||||||
#define DECLARE_OBJ_TYPE(x) void initialize(x*);
|
#define DECLARE_OBJ_TYPE(x) void initialize(x*);
|
||||||
#define DECLARE_OBJ_PROPERTY_METHODS(x) \
|
#define DECLARE_OBJ_PROPERTY_METHODS(x) \
|
||||||
@ -83,6 +85,8 @@ protected:
|
|||||||
//===============DECLARE PROPERTY VALUE TYPE==============================
|
//===============DECLARE PROPERTY VALUE TYPE==============================
|
||||||
QTableWidgetItem * addProperty(unsigned int value);
|
QTableWidgetItem * addProperty(unsigned int value);
|
||||||
QTableWidgetItem * addProperty(int value);
|
QTableWidgetItem * addProperty(int value);
|
||||||
|
QTableWidgetItem * addProperty(const MetaString & value);
|
||||||
|
QTableWidgetItem * addProperty(const TextIdentifier & value);
|
||||||
QTableWidgetItem * addProperty(const std::string & value);
|
QTableWidgetItem * addProperty(const std::string & value);
|
||||||
QTableWidgetItem * addProperty(const QString & value);
|
QTableWidgetItem * addProperty(const QString & value);
|
||||||
QTableWidgetItem * addProperty(const int3 & value);
|
QTableWidgetItem * addProperty(const int3 & value);
|
||||||
@ -144,7 +148,7 @@ protected:
|
|||||||
{
|
{
|
||||||
addProperty<T>(key, value, nullptr, restricted);
|
addProperty<T>(key, value, nullptr, restricted);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int row = 0;
|
int row = 0;
|
||||||
QTableWidget * table;
|
QTableWidget * table;
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
#include "../lib/mapObjects/CGPandoraBox.h"
|
#include "../lib/mapObjects/CGPandoraBox.h"
|
||||||
#include "../lib/mapObjects/CQuest.h"
|
#include "../lib/mapObjects/CQuest.h"
|
||||||
|
|
||||||
RewardsWidget::RewardsWidget(const CMap & m, CRewardableObject & p, QWidget *parent) :
|
RewardsWidget::RewardsWidget(CMap & m, CRewardableObject & p, QWidget *parent) :
|
||||||
QDialog(parent),
|
QDialog(parent),
|
||||||
map(m),
|
map(m),
|
||||||
object(p),
|
object(p),
|
||||||
@ -211,7 +211,7 @@ bool RewardsWidget::commitChanges()
|
|||||||
if(ui->onSelectText->text().isEmpty())
|
if(ui->onSelectText->text().isEmpty())
|
||||||
object.configuration.onSelect.clear();
|
object.configuration.onSelect.clear();
|
||||||
else
|
else
|
||||||
object.configuration.onSelect = MetaString::createFromRawString(ui->onSelectText->text().toStdString());
|
object.configuration.onSelect = MetaString::createFromTextID(mapRegisterLocalizedString("map", map, TextIdentifier("reward", object.instanceName, "onSelect"), ui->onSelectText->text().toStdString()));
|
||||||
object.configuration.canRefuse = ui->canRefuse->isChecked();
|
object.configuration.canRefuse = ui->canRefuse->isChecked();
|
||||||
|
|
||||||
//reset parameters
|
//reset parameters
|
||||||
@ -232,7 +232,7 @@ void RewardsWidget::saveCurrentVisitInfo(int index)
|
|||||||
if(ui->rewardMessage->text().isEmpty())
|
if(ui->rewardMessage->text().isEmpty())
|
||||||
vinfo.message.clear();
|
vinfo.message.clear();
|
||||||
else
|
else
|
||||||
vinfo.message = MetaString::createFromRawString(ui->rewardMessage->text().toStdString());
|
vinfo.message = MetaString::createFromTextID(mapRegisterLocalizedString("map", map, TextIdentifier("reward", object.instanceName, "info", index, "message"), ui->rewardMessage->text().toStdString()));
|
||||||
|
|
||||||
vinfo.reward.heroLevel = ui->rHeroLevel->value();
|
vinfo.reward.heroLevel = ui->rHeroLevel->value();
|
||||||
vinfo.reward.heroExperience = ui->rHeroExperience->value();
|
vinfo.reward.heroExperience = ui->rHeroExperience->value();
|
||||||
@ -649,7 +649,7 @@ void RewardsDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RewardsDelegate::RewardsDelegate(const CMap & m, CRewardableObject & t): map(m), object(t)
|
RewardsDelegate::RewardsDelegate(CMap & m, CRewardableObject & t): map(m), object(t)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ class RewardsWidget : public QDialog
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
explicit RewardsWidget(const CMap &, CRewardableObject &, QWidget *parent = nullptr);
|
explicit RewardsWidget(CMap &, CRewardableObject &, QWidget *parent = nullptr);
|
||||||
~RewardsWidget();
|
~RewardsWidget();
|
||||||
|
|
||||||
void obtainData();
|
void obtainData();
|
||||||
@ -64,14 +64,14 @@ private:
|
|||||||
|
|
||||||
Ui::RewardsWidget *ui;
|
Ui::RewardsWidget *ui;
|
||||||
CRewardableObject & object;
|
CRewardableObject & object;
|
||||||
const CMap & map;
|
CMap & map;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RewardsDelegate : public QStyledItemDelegate
|
class RewardsDelegate : public QStyledItemDelegate
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
RewardsDelegate(const CMap &, CRewardableObject &);
|
RewardsDelegate(CMap &, CRewardableObject &);
|
||||||
|
|
||||||
using QStyledItemDelegate::QStyledItemDelegate;
|
using QStyledItemDelegate::QStyledItemDelegate;
|
||||||
|
|
||||||
@ -82,5 +82,5 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
CRewardableObject & object;
|
CRewardableObject & object;
|
||||||
const CMap & map;
|
CMap & map;
|
||||||
};
|
};
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
#include "objectbrowser.h"
|
#include "objectbrowser.h"
|
||||||
#include "inspector/inspector.h"
|
#include "inspector/inspector.h"
|
||||||
#include "mapsettings/mapsettings.h"
|
#include "mapsettings/mapsettings.h"
|
||||||
|
#include "mapsettings/translations.h"
|
||||||
#include "playersettings.h"
|
#include "playersettings.h"
|
||||||
#include "validator.h"
|
#include "validator.h"
|
||||||
|
|
||||||
@ -300,6 +301,7 @@ void MainWindow::initializeMap(bool isNew)
|
|||||||
//enable settings
|
//enable settings
|
||||||
ui->actionMapSettings->setEnabled(true);
|
ui->actionMapSettings->setEnabled(true);
|
||||||
ui->actionPlayers_settings->setEnabled(true);
|
ui->actionPlayers_settings->setEnabled(true);
|
||||||
|
ui->actionTranslations->setEnabled(true);
|
||||||
|
|
||||||
//set minimal players count
|
//set minimal players count
|
||||||
if(isNew)
|
if(isNew)
|
||||||
@ -311,7 +313,7 @@ void MainWindow::initializeMap(bool isNew)
|
|||||||
onPlayersChanged();
|
onPlayersChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MainWindow::openMap(const QString & filenameSelect)
|
std::unique_ptr<CMap> MainWindow::openMapInternal(const QString & filenameSelect)
|
||||||
{
|
{
|
||||||
QFileInfo fi(filenameSelect);
|
QFileInfo fi(filenameSelect);
|
||||||
std::string fname = fi.fileName().toStdString();
|
std::string fname = fi.fileName().toStdString();
|
||||||
@ -325,26 +327,30 @@ bool MainWindow::openMap(const QString & filenameSelect)
|
|||||||
CResourceHandler::addFilesystem("local", "mapEditor", mapEditorFilesystem);
|
CResourceHandler::addFilesystem("local", "mapEditor", mapEditorFilesystem);
|
||||||
|
|
||||||
if(!CResourceHandler::get("mapEditor")->existsResource(resId))
|
if(!CResourceHandler::get("mapEditor")->existsResource(resId))
|
||||||
{
|
throw std::runtime_error("Cannot open map from this folder");
|
||||||
QMessageBox::warning(this, tr("Failed to open map"), tr("Cannot open map from this folder"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
CMapService mapService;
|
CMapService mapService;
|
||||||
|
if(auto header = mapService.loadMapHeader(resId))
|
||||||
|
{
|
||||||
|
auto missingMods = CMapService::verifyMapHeaderMods(*header);
|
||||||
|
ModIncompatibility::ModListWithVersion modList;
|
||||||
|
for(const auto & m : missingMods)
|
||||||
|
modList.push_back({m.second.name, m.second.version.toString()});
|
||||||
|
|
||||||
|
if(!modList.empty())
|
||||||
|
throw ModIncompatibility(modList);
|
||||||
|
|
||||||
|
return mapService.loadMap(resId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw std::runtime_error("Corrupted map");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MainWindow::openMap(const QString & filenameSelect)
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if(auto header = mapService.loadMapHeader(resId))
|
controller.setMap(openMapInternal(filenameSelect));
|
||||||
{
|
|
||||||
auto missingMods = CMapService::verifyMapHeaderMods(*header);
|
|
||||||
ModIncompatibility::ModListWithVersion modList;
|
|
||||||
for(const auto & m : missingMods)
|
|
||||||
modList.push_back({m.second.name, m.second.version.toString()});
|
|
||||||
|
|
||||||
if(!modList.empty())
|
|
||||||
throw ModIncompatibility(modList);
|
|
||||||
|
|
||||||
controller.setMap(mapService.loadMap(resId));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch(const ModIncompatibility & e)
|
catch(const ModIncompatibility & e)
|
||||||
{
|
{
|
||||||
@ -354,7 +360,7 @@ bool MainWindow::openMap(const QString & filenameSelect)
|
|||||||
}
|
}
|
||||||
catch(const std::exception & e)
|
catch(const std::exception & e)
|
||||||
{
|
{
|
||||||
QMessageBox::critical(this, "Failed to open map", e.what());
|
QMessageBox::critical(this, "Failed to open map", tr(e.what()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,6 +404,8 @@ void MainWindow::saveMap()
|
|||||||
else
|
else
|
||||||
QMessageBox::information(this, "Map validation", "Map has some errors. Open Validator from the Map menu to see issues found");
|
QMessageBox::information(this, "Map validation", "Map has some errors. Open Validator from the Map menu to see issues found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Translations::cleanupRemovedItems(*controller.map());
|
||||||
|
|
||||||
CMapService mapService;
|
CMapService mapService;
|
||||||
try
|
try
|
||||||
@ -419,7 +427,7 @@ void MainWindow::on_actionSave_as_triggered()
|
|||||||
if(!controller.map())
|
if(!controller.map())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto filenameSelect = QFileDialog::getSaveFileName(this, tr("Save map"), "", tr("VCMI maps (*.vmap)"));
|
auto filenameSelect = QFileDialog::getSaveFileName(this, tr("Save map"), lastSavingDir, tr("VCMI maps (*.vmap)"));
|
||||||
|
|
||||||
if(filenameSelect.isNull())
|
if(filenameSelect.isNull())
|
||||||
return;
|
return;
|
||||||
@ -428,6 +436,7 @@ void MainWindow::on_actionSave_as_triggered()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
filename = filenameSelect;
|
filename = filenameSelect;
|
||||||
|
lastSavingDir = filenameSelect.remove(QUrl(filenameSelect).fileName());
|
||||||
|
|
||||||
saveMap();
|
saveMap();
|
||||||
}
|
}
|
||||||
@ -445,16 +454,9 @@ void MainWindow::on_actionSave_triggered()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if(filename.isNull())
|
if(filename.isNull())
|
||||||
{
|
on_actionSave_as_triggered();
|
||||||
auto filenameSelect = QFileDialog::getSaveFileName(this, tr("Save map"), "", tr("VCMI maps (*.vmap)"));
|
else
|
||||||
|
saveMap();
|
||||||
if(filenameSelect.isNull())
|
|
||||||
return;
|
|
||||||
|
|
||||||
filename = filenameSelect;
|
|
||||||
}
|
|
||||||
|
|
||||||
saveMap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::terrainButtonClicked(TerrainId terrain)
|
void MainWindow::terrainButtonClicked(TerrainId terrain)
|
||||||
@ -1229,7 +1231,7 @@ void MainWindow::on_actionPaste_triggered()
|
|||||||
|
|
||||||
void MainWindow::on_actionExport_triggered()
|
void MainWindow::on_actionExport_triggered()
|
||||||
{
|
{
|
||||||
QString fileName = QFileDialog::getSaveFileName(this, tr("Save to image"), QCoreApplication::applicationDirPath(), "BMP (*.bmp);;JPEG (*.jpeg);;PNG (*.png)");
|
QString fileName = QFileDialog::getSaveFileName(this, tr("Save to image"), lastSavingDir, "BMP (*.bmp);;JPEG (*.jpeg);;PNG (*.png)");
|
||||||
if(!fileName.isNull())
|
if(!fileName.isNull())
|
||||||
{
|
{
|
||||||
QImage image(ui->mapView->scene()->sceneRect().size().toSize(), QImage::Format_RGB888);
|
QImage image(ui->mapView->scene()->sceneRect().size().toSize(), QImage::Format_RGB888);
|
||||||
@ -1239,3 +1241,39 @@ void MainWindow::on_actionExport_triggered()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MainWindow::on_actionTranslations_triggered()
|
||||||
|
{
|
||||||
|
auto translationsDialog = new Translations(*controller.map(), this);
|
||||||
|
translationsDialog->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_actionh3m_converter_triggered()
|
||||||
|
{
|
||||||
|
auto mapFiles = QFileDialog::getOpenFileNames(this, tr("Select maps to convert"),
|
||||||
|
QString::fromStdString(VCMIDirs::get().userCachePath().make_preferred().string()),
|
||||||
|
tr("HoMM3 maps(*.h3m)"));
|
||||||
|
if(mapFiles.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto saveDirectory = QFileDialog::getExistingDirectory(this, tr("Choose directory to save converted maps"), QCoreApplication::applicationDirPath());
|
||||||
|
if(saveDirectory.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for(auto & m : mapFiles)
|
||||||
|
{
|
||||||
|
CMapService mapService;
|
||||||
|
auto map = openMapInternal(m);
|
||||||
|
controller.repairMap(map.get());
|
||||||
|
mapService.saveMap(map, (saveDirectory + '/' + QFileInfo(m).completeBaseName() + ".vmap").toStdString());
|
||||||
|
}
|
||||||
|
QMessageBox::information(this, tr("Operation completed"), tr("Successfully converted %1 maps").arg(mapFiles.size()));
|
||||||
|
}
|
||||||
|
catch(const std::exception & e)
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, tr("Failed to convert the map. Abort operation"), tr(e.what()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,8 @@ class MainWindow : public QMainWindow
|
|||||||
#ifdef ENABLE_QT_TRANSLATIONS
|
#ifdef ENABLE_QT_TRANSLATIONS
|
||||||
QTranslator translator;
|
QTranslator translator;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
std::unique_ptr<CMap> openMapInternal(const QString &);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MainWindow(QWidget *parent = nullptr);
|
explicit MainWindow(QWidget *parent = nullptr);
|
||||||
@ -117,6 +119,10 @@ private slots:
|
|||||||
|
|
||||||
void on_actionExport_triggered();
|
void on_actionExport_triggered();
|
||||||
|
|
||||||
|
void on_actionTranslations_triggered();
|
||||||
|
|
||||||
|
void on_actionh3m_converter_triggered();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
void treeViewSelected(const QModelIndex &selected, const QModelIndex &deselected);
|
void treeViewSelected(const QModelIndex &selected, const QModelIndex &deselected);
|
||||||
@ -153,7 +159,7 @@ private:
|
|||||||
ObjectBrowserProxyModel * objectBrowser = nullptr;
|
ObjectBrowserProxyModel * objectBrowser = nullptr;
|
||||||
QGraphicsScene * scenePreview;
|
QGraphicsScene * scenePreview;
|
||||||
|
|
||||||
QString filename;
|
QString filename, lastSavingDir;
|
||||||
bool unsaved = false;
|
bool unsaved = false;
|
||||||
|
|
||||||
QStandardItemModel objectsModel;
|
QStandardItemModel objectsModel;
|
||||||
|
@ -14,20 +14,20 @@
|
|||||||
<string>VCMI Map Editor</string>
|
<string>VCMI Map Editor</string>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="centralwidget">
|
<widget class="QWidget" name="centralwidget">
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QVBoxLayout" name="verticalLayout_12">
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
<number>2</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="topMargin">
|
<property name="topMargin">
|
||||||
<number>2</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="rightMargin">
|
<property name="rightMargin">
|
||||||
<number>2</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="bottomMargin">
|
<property name="bottomMargin">
|
||||||
<number>2</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item row="0" column="0">
|
<item>
|
||||||
<widget class="MapView" name="mapView">
|
<widget class="MapView" name="mapView">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
@ -51,7 +51,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1024</width>
|
<width>1024</width>
|
||||||
<height>22</height>
|
<height>37</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QMenu" name="menuFile">
|
<widget class="QMenu" name="menuFile">
|
||||||
@ -63,6 +63,7 @@
|
|||||||
<addaction name="actionSave"/>
|
<addaction name="actionSave"/>
|
||||||
<addaction name="actionSave_as"/>
|
<addaction name="actionSave_as"/>
|
||||||
<addaction name="actionExport"/>
|
<addaction name="actionExport"/>
|
||||||
|
<addaction name="actionh3m_converter"/>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenu" name="menuMap">
|
<widget class="QMenu" name="menuMap">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
@ -70,6 +71,7 @@
|
|||||||
</property>
|
</property>
|
||||||
<addaction name="actionMapSettings"/>
|
<addaction name="actionMapSettings"/>
|
||||||
<addaction name="actionPlayers_settings"/>
|
<addaction name="actionPlayers_settings"/>
|
||||||
|
<addaction name="actionTranslations"/>
|
||||||
<addaction name="actionValidate"/>
|
<addaction name="actionValidate"/>
|
||||||
<addaction name="actionUpdate_appearance"/>
|
<addaction name="actionUpdate_appearance"/>
|
||||||
<addaction name="actionRecreate_obstacles"/>
|
<addaction name="actionRecreate_obstacles"/>
|
||||||
@ -140,6 +142,7 @@
|
|||||||
<addaction name="actionPaste"/>
|
<addaction name="actionPaste"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="actionFill"/>
|
<addaction name="actionFill"/>
|
||||||
|
<addaction name="actionTranslations"/>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QDockWidget" name="dockWidget_2">
|
<widget class="QDockWidget" name="dockWidget_2">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -156,7 +159,7 @@
|
|||||||
</property>
|
</property>
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>192</width>
|
<width>524287</width>
|
||||||
<height>214</height>
|
<height>214</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
@ -219,7 +222,7 @@
|
|||||||
</widget>
|
</widget>
|
||||||
<widget class="QDockWidget" name="dockWidget_3">
|
<widget class="QDockWidget" name="dockWidget_3">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
@ -244,7 +247,7 @@
|
|||||||
</attribute>
|
</attribute>
|
||||||
<widget class="QWidget" name="dockWidgetContents_3">
|
<widget class="QWidget" name="dockWidgetContents_3">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
@ -271,7 +274,7 @@
|
|||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>0</number>
|
<number>1</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="tab_2">
|
<widget class="QWidget" name="tab_2">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -429,11 +432,17 @@
|
|||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>128</width>
|
<width>128</width>
|
||||||
<height>496</height>
|
<height>192</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>524287</width>
|
||||||
|
<height>192</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Terrains View</string>
|
<string>Tools</string>
|
||||||
</property>
|
</property>
|
||||||
<attribute name="dockWidgetArea">
|
<attribute name="dockWidgetArea">
|
||||||
<number>1</number>
|
<number>1</number>
|
||||||
@ -473,7 +482,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="groupBox">
|
<widget class="QGroupBox" name="groupBox">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
@ -488,6 +497,18 @@
|
|||||||
<string>Brush</string>
|
<string>Brush</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QFormLayout" name="formLayout">
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QPushButton" name="toolBrush">
|
<widget class="QPushButton" name="toolBrush">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -731,6 +752,36 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<widget class="QDockWidget" name="dockWidget_4">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Painting</string>
|
||||||
|
</property>
|
||||||
|
<attribute name="dockWidgetArea">
|
||||||
|
<number>1</number>
|
||||||
|
</attribute>
|
||||||
|
<widget class="QWidget" name="dockWidgetContents_6">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_9">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QToolBox" name="toolBox">
|
<widget class="QToolBox" name="toolBox">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -754,7 +805,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>128</width>
|
<width>128</width>
|
||||||
<height>251</height>
|
<height>192</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -797,7 +848,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>128</width>
|
<width>128</width>
|
||||||
<height>251</height>
|
<height>192</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -833,7 +884,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>128</width>
|
<width>128</width>
|
||||||
<height>251</height>
|
<height>192</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -865,6 +916,36 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<widget class="QDockWidget" name="dockWidget_5">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>524287</width>
|
||||||
|
<height>150</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Preview</string>
|
||||||
|
</property>
|
||||||
|
<attribute name="dockWidgetArea">
|
||||||
|
<number>1</number>
|
||||||
|
</attribute>
|
||||||
|
<widget class="QWidget" name="dockWidgetContents_7">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_11">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGraphicsView" name="objectPreview">
|
<widget class="QGraphicsView" name="objectPreview">
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
@ -1237,6 +1318,29 @@
|
|||||||
<string>Export as...</string>
|
<string>Export as...</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionTranslations">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset>
|
||||||
|
<normaloff>icons:translations.png</normaloff>icons:translations.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Translations</string>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>Ctrl+T</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionh3m_converter">
|
||||||
|
<property name="text">
|
||||||
|
<string>h3m converter</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>h3m converter</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
|
@ -86,26 +86,46 @@ MinimapScene * MapController::miniScene(int level)
|
|||||||
|
|
||||||
void MapController::repairMap()
|
void MapController::repairMap()
|
||||||
{
|
{
|
||||||
|
repairMap(map());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapController::repairMap(CMap * map) const
|
||||||
|
{
|
||||||
|
if(!map)
|
||||||
|
return;
|
||||||
|
|
||||||
//there might be extra skills, arts and spells not imported from map
|
//there might be extra skills, arts and spells not imported from map
|
||||||
if(VLC->skillh->getDefaultAllowed().size() > map()->allowedAbilities.size())
|
if(VLC->skillh->getDefaultAllowed().size() > map->allowedAbilities.size())
|
||||||
{
|
{
|
||||||
map()->allowedAbilities.resize(VLC->skillh->getDefaultAllowed().size());
|
map->allowedAbilities.resize(VLC->skillh->getDefaultAllowed().size());
|
||||||
}
|
}
|
||||||
if(VLC->arth->getDefaultAllowed().size() > map()->allowedArtifact.size())
|
if(VLC->arth->getDefaultAllowed().size() > map->allowedArtifact.size())
|
||||||
{
|
{
|
||||||
map()->allowedArtifact.resize(VLC->arth->getDefaultAllowed().size());
|
map->allowedArtifact.resize(VLC->arth->getDefaultAllowed().size());
|
||||||
}
|
}
|
||||||
if(VLC->spellh->getDefaultAllowed().size() > map()->allowedSpells.size())
|
if(VLC->spellh->getDefaultAllowed().size() > map->allowedSpells.size())
|
||||||
{
|
{
|
||||||
map()->allowedSpells.resize(VLC->spellh->getDefaultAllowed().size());
|
map->allowedSpells.resize(VLC->spellh->getDefaultAllowed().size());
|
||||||
}
|
}
|
||||||
if(VLC->heroh->getDefaultAllowed().size() > map()->allowedHeroes.size())
|
if(VLC->heroh->getDefaultAllowed().size() > map->allowedHeroes.size())
|
||||||
{
|
{
|
||||||
map()->allowedHeroes.resize(VLC->heroh->getDefaultAllowed().size());
|
map->allowedHeroes.resize(VLC->heroh->getDefaultAllowed().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//make sure events/rumors has name to have proper identifiers
|
||||||
|
int emptyNameId = 1;
|
||||||
|
for(auto & e : map->events)
|
||||||
|
if(e.name.empty())
|
||||||
|
e.name = "event_" + std::to_string(emptyNameId++);
|
||||||
|
emptyNameId = 1;
|
||||||
|
for(auto & e : map->rumors)
|
||||||
|
if(e.name.empty())
|
||||||
|
e.name = "rumor_" + std::to_string(emptyNameId++);
|
||||||
|
|
||||||
//fix owners for objects
|
//fix owners for objects
|
||||||
for(auto obj : _map->objects)
|
auto allImpactedObjects(map->objects);
|
||||||
|
allImpactedObjects.insert(allImpactedObjects.end(), map->predefinedHeroes.begin(), map->predefinedHeroes.end());
|
||||||
|
for(auto obj : allImpactedObjects)
|
||||||
{
|
{
|
||||||
//setup proper names (hero name will be fixed later
|
//setup proper names (hero name will be fixed later
|
||||||
if(obj->ID != Obj::HERO && obj->ID != Obj::PRISON && (obj->typeName.empty() || obj->subTypeName.empty()))
|
if(obj->ID != Obj::HERO && obj->ID != Obj::PRISON && (obj->typeName.empty() || obj->subTypeName.empty()))
|
||||||
@ -129,7 +149,7 @@ void MapController::repairMap()
|
|||||||
//fix hero instance
|
//fix hero instance
|
||||||
if(auto * nih = dynamic_cast<CGHeroInstance*>(obj.get()))
|
if(auto * nih = dynamic_cast<CGHeroInstance*>(obj.get()))
|
||||||
{
|
{
|
||||||
map()->allowedHeroes.at(nih->subID) = true;
|
map->allowedHeroes.at(nih->subID) = true;
|
||||||
auto type = VLC->heroh->objects[nih->subID];
|
auto type = VLC->heroh->objects[nih->subID];
|
||||||
assert(type->heroClass);
|
assert(type->heroClass);
|
||||||
//TODO: find a way to get proper type name
|
//TODO: find a way to get proper type name
|
||||||
@ -154,12 +174,16 @@ void MapController::repairMap()
|
|||||||
if(nih->spellbookContainsSpell(SpellID::PRESET))
|
if(nih->spellbookContainsSpell(SpellID::PRESET))
|
||||||
{
|
{
|
||||||
nih->removeSpellFromSpellbook(SpellID::PRESET);
|
nih->removeSpellFromSpellbook(SpellID::PRESET);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for(auto spellID : type->spells)
|
for(auto spellID : type->spells)
|
||||||
nih->addSpellToSpellbook(spellID);
|
nih->addSpellToSpellbook(spellID);
|
||||||
}
|
}
|
||||||
|
if(nih->spellbookContainsSpell(SpellID::SPELLBOOK_PRESET))
|
||||||
|
{
|
||||||
|
nih->removeSpellFromSpellbook(SpellID::SPELLBOOK_PRESET);
|
||||||
|
if(!nih->getArt(ArtifactPosition::SPELLBOOK) && type->haveSpellBook)
|
||||||
|
nih->putArtifact(ArtifactPosition::SPELLBOOK, ArtifactUtils::createNewArtifactInstance(ArtifactID::SPELLBOOK));
|
||||||
|
}
|
||||||
|
|
||||||
//fix portrait
|
//fix portrait
|
||||||
if(nih->portrait < 0 || nih->portrait == 255)
|
if(nih->portrait < 0 || nih->portrait == 255)
|
||||||
nih->portrait = type->imageIndex;
|
nih->portrait = type->imageIndex;
|
||||||
@ -196,7 +220,7 @@ void MapController::repairMap()
|
|||||||
art->storedArtifact = a;
|
art->storedArtifact = a;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
map()->allowedArtifact.at(art->subID) = true;
|
map->allowedArtifact.at(art->subID) = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,8 +30,9 @@ public:
|
|||||||
~MapController();
|
~MapController();
|
||||||
|
|
||||||
void setMap(std::unique_ptr<CMap>);
|
void setMap(std::unique_ptr<CMap>);
|
||||||
void initObstaclePainters(CMap* map);
|
void initObstaclePainters(CMap * map);
|
||||||
|
|
||||||
|
void repairMap(CMap * map) const;
|
||||||
void repairMap();
|
void repairMap();
|
||||||
|
|
||||||
const std::unique_ptr<CMap> & getMapUniquePtr() const; //to be used for map saving
|
const std::unique_ptr<CMap> & getMapUniquePtr() const; //to be used for map saving
|
||||||
|
@ -37,7 +37,7 @@ QVariant toVariant(const CMapEvent & event)
|
|||||||
{
|
{
|
||||||
QVariantMap result;
|
QVariantMap result;
|
||||||
result["name"] = QString::fromStdString(event.name);
|
result["name"] = QString::fromStdString(event.name);
|
||||||
result["message"] = QString::fromStdString(event.message);
|
result["message"] = QString::fromStdString(event.message.toString());
|
||||||
result["players"] = QVariant::fromValue(event.players);
|
result["players"] = QVariant::fromValue(event.players);
|
||||||
result["humanAffected"] = QVariant::fromValue(event.humanAffected);
|
result["humanAffected"] = QVariant::fromValue(event.humanAffected);
|
||||||
result["computerAffected"] = QVariant::fromValue(event.computerAffected);
|
result["computerAffected"] = QVariant::fromValue(event.computerAffected);
|
||||||
@ -47,12 +47,12 @@ QVariant toVariant(const CMapEvent & event)
|
|||||||
return QVariant(result);
|
return QVariant(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
CMapEvent eventFromVariant(const QVariant & variant)
|
CMapEvent eventFromVariant(CMapHeader & mapHeader, const QVariant & variant)
|
||||||
{
|
{
|
||||||
CMapEvent result;
|
CMapEvent result;
|
||||||
auto v = variant.toMap();
|
auto v = variant.toMap();
|
||||||
result.name = v.value("name").toString().toStdString();
|
result.name = v.value("name").toString().toStdString();
|
||||||
result.message = v.value("message").toString().toStdString();
|
result.message.appendTextID(mapRegisterLocalizedString("map", mapHeader, TextIdentifier("header", "event", result.name, "message"), v.value("message").toString().toStdString()));
|
||||||
result.players = v.value("players").toInt();
|
result.players = v.value("players").toInt();
|
||||||
result.humanAffected = v.value("humanAffected").toInt();
|
result.humanAffected = v.value("humanAffected").toInt();
|
||||||
result.computerAffected = v.value("computerAffected").toInt();
|
result.computerAffected = v.value("computerAffected").toInt();
|
||||||
@ -91,7 +91,7 @@ void EventSettings::update()
|
|||||||
for(int i = 0; i < ui->eventsList->count(); ++i)
|
for(int i = 0; i < ui->eventsList->count(); ++i)
|
||||||
{
|
{
|
||||||
const auto * item = ui->eventsList->item(i);
|
const auto * item = ui->eventsList->item(i);
|
||||||
controller->map()->events.push_back(eventFromVariant(item->data(Qt::UserRole)));
|
controller->map()->events.push_back(eventFromVariant(*controller->map(), item->data(Qt::UserRole)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,8 +27,8 @@ GeneralSettings::~GeneralSettings()
|
|||||||
void GeneralSettings::initialize(MapController & c)
|
void GeneralSettings::initialize(MapController & c)
|
||||||
{
|
{
|
||||||
AbstractSettings::initialize(c);
|
AbstractSettings::initialize(c);
|
||||||
ui->mapNameEdit->setText(tr(controller->map()->name.c_str()));
|
ui->mapNameEdit->setText(QString::fromStdString(controller->map()->name.toString()));
|
||||||
ui->mapDescriptionEdit->setPlainText(tr(controller->map()->description.c_str()));
|
ui->mapDescriptionEdit->setPlainText(QString::fromStdString(controller->map()->description.toString()));
|
||||||
ui->heroLevelLimit->setValue(controller->map()->levelLimit);
|
ui->heroLevelLimit->setValue(controller->map()->levelLimit);
|
||||||
ui->heroLevelLimitCheck->setChecked(controller->map()->levelLimit);
|
ui->heroLevelLimitCheck->setChecked(controller->map()->levelLimit);
|
||||||
|
|
||||||
@ -59,8 +59,8 @@ void GeneralSettings::initialize(MapController & c)
|
|||||||
|
|
||||||
void GeneralSettings::update()
|
void GeneralSettings::update()
|
||||||
{
|
{
|
||||||
controller->map()->name = ui->mapNameEdit->text().toStdString();
|
controller->map()->name = MetaString::createFromTextID(mapRegisterLocalizedString("map", *controller->map(), TextIdentifier("header", "name"), ui->mapNameEdit->text().toStdString()));
|
||||||
controller->map()->description = ui->mapDescriptionEdit->toPlainText().toStdString();
|
controller->map()->description = MetaString::createFromTextID(mapRegisterLocalizedString("map", *controller->map(), TextIdentifier("header", "description"), ui->mapDescriptionEdit->toPlainText().toStdString()));
|
||||||
if(ui->heroLevelLimitCheck->isChecked())
|
if(ui->heroLevelLimitCheck->isChecked())
|
||||||
controller->map()->levelLimit = ui->heroLevelLimit->value();
|
controller->map()->levelLimit = ui->heroLevelLimit->value();
|
||||||
else
|
else
|
||||||
|
@ -243,7 +243,7 @@ void LoseConditions::on_loseComboBox_currentIndexChanged(int index)
|
|||||||
loseTypeWidget = new QComboBox;
|
loseTypeWidget = new QComboBox;
|
||||||
ui->loseParamsLayout->addWidget(loseTypeWidget);
|
ui->loseParamsLayout->addWidget(loseTypeWidget);
|
||||||
for(int i : getObjectIndexes<const CGTownInstance>(*controller->map()))
|
for(int i : getObjectIndexes<const CGTownInstance>(*controller->map()))
|
||||||
loseTypeWidget->addItem(tr(getTownName(*controller->map(), i).c_str()), QVariant::fromValue(i));
|
loseTypeWidget->addItem(QString::fromStdString(getTownName(*controller->map(), i).c_str()), QVariant::fromValue(i));
|
||||||
pickObjectButton = new QToolButton;
|
pickObjectButton = new QToolButton;
|
||||||
connect(pickObjectButton, &QToolButton::clicked, this, &LoseConditions::onObjectSelect);
|
connect(pickObjectButton, &QToolButton::clicked, this, &LoseConditions::onObjectSelect);
|
||||||
ui->loseParamsLayout->addWidget(pickObjectButton);
|
ui->loseParamsLayout->addWidget(pickObjectButton);
|
||||||
@ -254,7 +254,7 @@ void LoseConditions::on_loseComboBox_currentIndexChanged(int index)
|
|||||||
loseTypeWidget = new QComboBox;
|
loseTypeWidget = new QComboBox;
|
||||||
ui->loseParamsLayout->addWidget(loseTypeWidget);
|
ui->loseParamsLayout->addWidget(loseTypeWidget);
|
||||||
for(int i : getObjectIndexes<const CGHeroInstance>(*controller->map()))
|
for(int i : getObjectIndexes<const CGHeroInstance>(*controller->map()))
|
||||||
loseTypeWidget->addItem(tr(getHeroName(*controller->map(), i).c_str()), QVariant::fromValue(i));
|
loseTypeWidget->addItem(QString::fromStdString(getHeroName(*controller->map(), i).c_str()), QVariant::fromValue(i));
|
||||||
pickObjectButton = new QToolButton;
|
pickObjectButton = new QToolButton;
|
||||||
connect(pickObjectButton, &QToolButton::clicked, this, &LoseConditions::onObjectSelect);
|
connect(pickObjectButton, &QToolButton::clicked, this, &LoseConditions::onObjectSelect);
|
||||||
ui->loseParamsLayout->addWidget(pickObjectButton);
|
ui->loseParamsLayout->addWidget(pickObjectButton);
|
||||||
|
@ -30,7 +30,7 @@ void RumorSettings::initialize(MapController & c)
|
|||||||
for(auto & rumor : controller->map()->rumors)
|
for(auto & rumor : controller->map()->rumors)
|
||||||
{
|
{
|
||||||
auto * item = new QListWidgetItem(QString::fromStdString(rumor.name));
|
auto * item = new QListWidgetItem(QString::fromStdString(rumor.name));
|
||||||
item->setData(Qt::UserRole, QVariant(QString::fromStdString(rumor.text)));
|
item->setData(Qt::UserRole, QVariant(QString::fromStdString(rumor.text.toString())));
|
||||||
item->setFlags(item->flags() | Qt::ItemIsEditable);
|
item->setFlags(item->flags() | Qt::ItemIsEditable);
|
||||||
ui->rumors->addItem(item);
|
ui->rumors->addItem(item);
|
||||||
}
|
}
|
||||||
@ -43,7 +43,7 @@ void RumorSettings::update()
|
|||||||
{
|
{
|
||||||
Rumor rumor;
|
Rumor rumor;
|
||||||
rumor.name = ui->rumors->item(i)->text().toStdString();
|
rumor.name = ui->rumors->item(i)->text().toStdString();
|
||||||
rumor.text = ui->rumors->item(i)->data(Qt::UserRole).toString().toStdString();
|
rumor.text.appendTextID(mapRegisterLocalizedString("map", *controller->map(), TextIdentifier("header", "rumor", i, "text"), ui->rumors->item(i)->data(Qt::UserRole).toString().toStdString()));
|
||||||
controller->map()->rumors.push_back(rumor);
|
controller->map()->rumors.push_back(rumor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
198
mapeditor/mapsettings/translations.cpp
Normal file
198
mapeditor/mapsettings/translations.cpp
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
/*
|
||||||
|
* translations.cpp, part of VCMI engine
|
||||||
|
*
|
||||||
|
* Authors: listed in file AUTHORS in main folder
|
||||||
|
*
|
||||||
|
* License: GNU General Public License v2.0 or later
|
||||||
|
* Full text of license available in license.txt file, in main folder
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "StdInc.h"
|
||||||
|
#include "translations.h"
|
||||||
|
#include "ui_translations.h"
|
||||||
|
#include "../../lib/Languages.h"
|
||||||
|
#include "../../lib/CGeneralTextHandler.h"
|
||||||
|
#include "../../lib/VCMI_Lib.h"
|
||||||
|
|
||||||
|
void Translations::cleanupRemovedItems(CMap & map)
|
||||||
|
{
|
||||||
|
std::set<std::string> existingObjects;
|
||||||
|
for(auto object : map.objects)
|
||||||
|
existingObjects.insert(object->instanceName);
|
||||||
|
|
||||||
|
for(auto & translations : map.translations.Struct())
|
||||||
|
{
|
||||||
|
auto updateTranslations = JsonNode(JsonNode::JsonType::DATA_STRUCT);
|
||||||
|
for(auto & s : translations.second.Struct())
|
||||||
|
{
|
||||||
|
for(auto part : QString::fromStdString(s.first).split('.'))
|
||||||
|
{
|
||||||
|
if(part == "map" || existingObjects.count(part.toStdString()))
|
||||||
|
{
|
||||||
|
updateTranslations.Struct()[s.first] = s.second;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
translations.second = updateTranslations;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Translations::cleanupRemovedItems(CMap & map, const std::string & pattern)
|
||||||
|
{
|
||||||
|
for(auto & translations : map.translations.Struct())
|
||||||
|
{
|
||||||
|
auto updateTranslations = JsonNode(JsonNode::JsonType::DATA_STRUCT);
|
||||||
|
for(auto & s : translations.second.Struct())
|
||||||
|
{
|
||||||
|
if(s.first.find(pattern) == std::string::npos)
|
||||||
|
updateTranslations.Struct()[s.first] = s.second;
|
||||||
|
}
|
||||||
|
translations.second = updateTranslations;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Translations::Translations(CMapHeader & mh, QWidget *parent) :
|
||||||
|
QDialog(parent),
|
||||||
|
ui(new Ui::Translations),
|
||||||
|
mapHeader(mh)
|
||||||
|
{
|
||||||
|
setAttribute(Qt::WA_DeleteOnClose, true);
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
//fill languages list
|
||||||
|
std::set<int> indexFoundLang;
|
||||||
|
int foundLang = -1;
|
||||||
|
ui->languageSelect->blockSignals(true);
|
||||||
|
for(auto & language : Languages::getLanguageList())
|
||||||
|
{
|
||||||
|
ui->languageSelect->addItem(QString("%1 (%2)").arg(QString::fromStdString(language.nameEnglish), QString::fromStdString(language.nameNative)));
|
||||||
|
ui->languageSelect->setItemData(ui->languageSelect->count() - 1, QVariant(QString::fromStdString(language.identifier)));
|
||||||
|
if(mapHeader.translations.Struct().count(language.identifier) && !mapHeader.translations[language.identifier].Struct().empty())
|
||||||
|
indexFoundLang.insert(ui->languageSelect->count() - 1);
|
||||||
|
if(language.identifier == VLC->generaltexth->getPreferredLanguage())
|
||||||
|
foundLang = ui->languageSelect->count() - 1;
|
||||||
|
}
|
||||||
|
ui->languageSelect->blockSignals(false);
|
||||||
|
|
||||||
|
if(foundLang >= 0 && !indexFoundLang.empty() && !indexFoundLang.count(foundLang))
|
||||||
|
{
|
||||||
|
foundLang = *indexFoundLang.begin();
|
||||||
|
mapPreferredLanguage = ui->languageSelect->itemData(foundLang).toString().toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(foundLang >= 0)
|
||||||
|
ui->languageSelect->setCurrentIndex(foundLang);
|
||||||
|
|
||||||
|
if(mapPreferredLanguage.empty())
|
||||||
|
mapPreferredLanguage = VLC->generaltexth->getPreferredLanguage();
|
||||||
|
}
|
||||||
|
|
||||||
|
Translations::~Translations()
|
||||||
|
{
|
||||||
|
mapHeader.registerMapStrings();
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Translations::fillTranslationsTable(const std::string & language)
|
||||||
|
{
|
||||||
|
Translations::cleanupRemovedItems(dynamic_cast<CMap&>(mapHeader));
|
||||||
|
auto & translation = mapHeader.translations[language];
|
||||||
|
ui->translationsTable->blockSignals(true);
|
||||||
|
ui->translationsTable->setRowCount(0);
|
||||||
|
ui->translationsTable->setRowCount(translation.Struct().size());
|
||||||
|
int i = 0;
|
||||||
|
for(auto & s : translation.Struct())
|
||||||
|
{
|
||||||
|
auto textLines = QString::fromStdString(s.second.String());
|
||||||
|
textLines = textLines.replace('\n', "\\n");
|
||||||
|
|
||||||
|
auto * wId = new QTableWidgetItem(QString::fromStdString(s.first));
|
||||||
|
auto * wText = new QTableWidgetItem(textLines);
|
||||||
|
wId->setFlags(wId->flags() & ~Qt::ItemIsEditable);
|
||||||
|
wText->setFlags(wId->flags() | Qt::ItemIsEditable);
|
||||||
|
ui->translationsTable->setItem(i, 0, wId);
|
||||||
|
ui->translationsTable->setItem(i++, 1, wText);
|
||||||
|
}
|
||||||
|
ui->translationsTable->resizeColumnToContents(0);
|
||||||
|
ui->translationsTable->blockSignals(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Translations::on_languageSelect_currentIndexChanged(int index)
|
||||||
|
{
|
||||||
|
auto language = ui->languageSelect->currentData().toString().toStdString();
|
||||||
|
bool hasLanguage = mapHeader.translations.Struct().count(language);
|
||||||
|
ui->supportedCheck->blockSignals(true);
|
||||||
|
ui->supportedCheck->setChecked(hasLanguage);
|
||||||
|
ui->supportedCheck->blockSignals(false);
|
||||||
|
ui->translationsTable->setEnabled(hasLanguage);
|
||||||
|
if(hasLanguage)
|
||||||
|
fillTranslationsTable(language);
|
||||||
|
else
|
||||||
|
ui->translationsTable->setRowCount(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Translations::on_supportedCheck_toggled(bool checked)
|
||||||
|
{
|
||||||
|
auto language = ui->languageSelect->currentData().toString().toStdString();
|
||||||
|
auto & translation = mapHeader.translations[language];
|
||||||
|
bool hasRecord = !translation.Struct().empty();
|
||||||
|
|
||||||
|
if(checked)
|
||||||
|
{
|
||||||
|
//copy from default language
|
||||||
|
translation = mapHeader.translations[mapPreferredLanguage];
|
||||||
|
|
||||||
|
fillTranslationsTable(language);
|
||||||
|
ui->translationsTable->setEnabled(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool canRemove = language != mapPreferredLanguage;
|
||||||
|
if(!canRemove)
|
||||||
|
{
|
||||||
|
QMessageBox::information(this, tr("Remove translation"), tr("Default language cannot be removed"));
|
||||||
|
}
|
||||||
|
else if(hasRecord)
|
||||||
|
{
|
||||||
|
auto sure = QMessageBox::question(this, tr("Remove translation"), tr("All existing text records for this language will be removed. Continue?"));
|
||||||
|
canRemove = sure != QMessageBox::No;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!canRemove)
|
||||||
|
{
|
||||||
|
ui->supportedCheck->blockSignals(true);
|
||||||
|
ui->supportedCheck->setChecked(true);
|
||||||
|
ui->supportedCheck->blockSignals(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ui->translationsTable->blockSignals(true);
|
||||||
|
ui->translationsTable->setRowCount(0);
|
||||||
|
translation = JsonNode(JsonNode::JsonType::DATA_NULL);
|
||||||
|
ui->translationsTable->blockSignals(false);
|
||||||
|
ui->translationsTable->setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Translations::on_translationsTable_itemChanged(QTableWidgetItem * item)
|
||||||
|
{
|
||||||
|
assert(item->column() == 1);
|
||||||
|
|
||||||
|
auto language = ui->languageSelect->currentData().toString().toStdString();
|
||||||
|
auto & translation = mapHeader.translations[language];
|
||||||
|
|
||||||
|
assert(!translation.isNull());
|
||||||
|
|
||||||
|
auto textId = ui->translationsTable->item(item->row(), 0)->text().toStdString();
|
||||||
|
assert(!textId.empty());
|
||||||
|
if(textId.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto textLines = item->text();
|
||||||
|
textLines = textLines.replace("\\n", "\n");
|
||||||
|
translation[textId].String() = textLines.toStdString();
|
||||||
|
}
|
||||||
|
|
45
mapeditor/mapsettings/translations.h
Normal file
45
mapeditor/mapsettings/translations.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* translations.h, part of VCMI engine
|
||||||
|
*
|
||||||
|
* Authors: listed in file AUTHORS in main folder
|
||||||
|
*
|
||||||
|
* License: GNU General Public License v2.0 or later
|
||||||
|
* Full text of license available in license.txt file, in main folder
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include "../lib/mapping/CMap.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class Translations;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Translations : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
void fillTranslationsTable(const std::string & language);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Translations(CMapHeader & mapHeader, QWidget *parent = nullptr);
|
||||||
|
~Translations();
|
||||||
|
|
||||||
|
//removes unused string IDs from map translations
|
||||||
|
static void cleanupRemovedItems(CMap & map);
|
||||||
|
static void cleanupRemovedItems(CMap & map, const std::string & pattern);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void on_languageSelect_currentIndexChanged(int index);
|
||||||
|
|
||||||
|
void on_supportedCheck_toggled(bool checked);
|
||||||
|
|
||||||
|
void on_translationsTable_itemChanged(QTableWidgetItem *item);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::Translations *ui;
|
||||||
|
CMapHeader & mapHeader;
|
||||||
|
std::string mapPreferredLanguage;
|
||||||
|
};
|
84
mapeditor/mapsettings/translations.ui
Normal file
84
mapeditor/mapsettings/translations.ui
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Translations</class>
|
||||||
|
<widget class="QDialog" name="Translations">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>989</width>
|
||||||
|
<height>641</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Map translations</string>
|
||||||
|
</property>
|
||||||
|
<property name="modal">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Language</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="languageSelect">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="supportedCheck">
|
||||||
|
<property name="text">
|
||||||
|
<string>Suppported</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QTableWidget" name="translationsTable">
|
||||||
|
<attribute name="horizontalHeaderDefaultSectionSize">
|
||||||
|
<number>240</number>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="horizontalHeaderStretchLastSection">
|
||||||
|
<bool>true</bool>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="verticalHeaderVisible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="verticalHeaderDefaultSectionSize">
|
||||||
|
<number>24</number>
|
||||||
|
</attribute>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>String ID</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>Text</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
@ -90,6 +90,11 @@ void WindowNewMap::loadUserSettings()
|
|||||||
{
|
{
|
||||||
ui->heightTxt->setText(height.toString());
|
ui->heightTxt->setText(height.toString());
|
||||||
}
|
}
|
||||||
|
for(auto & sz : mapSizes)
|
||||||
|
{
|
||||||
|
if(sz.second.first == width.toInt() && sz.second.second == height.toInt())
|
||||||
|
ui->sizeCombo->setCurrentIndex(sz.first);
|
||||||
|
}
|
||||||
auto twoLevel = s.value(newMapTwoLevel);
|
auto twoLevel = s.value(newMapTwoLevel);
|
||||||
if (twoLevel.isValid())
|
if (twoLevel.isValid())
|
||||||
{
|
{
|
||||||
|
@ -2738,7 +2738,7 @@ bool CGameHandler::moveArtifact(const ArtifactLocation &al1, const ArtifactLocat
|
|||||||
}
|
}
|
||||||
|
|
||||||
MoveArtifact ma(&src, &dst);
|
MoveArtifact ma(&src, &dst);
|
||||||
if(dst.slot == ArtifactPosition::TRANSITION_POS)
|
if(src.artHolder == dst.artHolder)
|
||||||
ma.askAssemble = false;
|
ma.askAssemble = false;
|
||||||
sendAndApply(&ma);
|
sendAndApply(&ma);
|
||||||
}
|
}
|
||||||
@ -2853,7 +2853,7 @@ bool CGameHandler::bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID
|
|||||||
* @param assembleTo If assemble is true, this represents the artifact ID of the combination
|
* @param assembleTo If assemble is true, this represents the artifact ID of the combination
|
||||||
* artifact to assemble to. Otherwise it's not used.
|
* artifact to assemble to. Otherwise it's not used.
|
||||||
*/
|
*/
|
||||||
bool CGameHandler::assembleArtifacts (ObjectInstanceID heroID, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo)
|
bool CGameHandler::assembleArtifacts(ObjectInstanceID heroID, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo)
|
||||||
{
|
{
|
||||||
const CGHeroInstance * hero = getHero(heroID);
|
const CGHeroInstance * hero = getHero(heroID);
|
||||||
const CArtifactInstance * destArtifact = hero->getArt(artifactSlot);
|
const CArtifactInstance * destArtifact = hero->getArt(artifactSlot);
|
||||||
@ -2861,23 +2861,27 @@ bool CGameHandler::assembleArtifacts (ObjectInstanceID heroID, ArtifactPosition
|
|||||||
if(!destArtifact)
|
if(!destArtifact)
|
||||||
COMPLAIN_RET("assembleArtifacts: there is no such artifact instance!");
|
COMPLAIN_RET("assembleArtifacts: there is no such artifact instance!");
|
||||||
|
|
||||||
|
const auto dstLoc = ArtifactLocation(hero, artifactSlot);
|
||||||
if(assemble)
|
if(assemble)
|
||||||
{
|
{
|
||||||
CArtifact * combinedArt = VLC->arth->objects[assembleTo];
|
CArtifact * combinedArt = VLC->arth->objects[assembleTo];
|
||||||
if(!combinedArt->isCombined())
|
if(!combinedArt->isCombined())
|
||||||
COMPLAIN_RET("assembleArtifacts: Artifact being attempted to assemble is not a combined artifacts!");
|
COMPLAIN_RET("assembleArtifacts: Artifact being attempted to assemble is not a combined artifacts!");
|
||||||
if (!vstd::contains(ArtifactUtils::assemblyPossibilities(hero, destArtifact->getTypeId(),
|
if(!vstd::contains(ArtifactUtils::assemblyPossibilities(hero, destArtifact->getTypeId()), combinedArt))
|
||||||
ArtifactUtils::isSlotEquipment(artifactSlot)), combinedArt))
|
|
||||||
{
|
{
|
||||||
COMPLAIN_RET("assembleArtifacts: It's impossible to assemble requested artifact!");
|
COMPLAIN_RET("assembleArtifacts: It's impossible to assemble requested artifact!");
|
||||||
}
|
}
|
||||||
|
if(!destArtifact->canBePutAt(dstLoc)
|
||||||
|
&& !destArtifact->canBePutAt(ArtifactLocation(hero, ArtifactPosition::BACKPACK_START)))
|
||||||
|
{
|
||||||
|
COMPLAIN_RET("assembleArtifacts: It's impossible to give the artholder requested artifact!");
|
||||||
|
}
|
||||||
|
|
||||||
if(ArtifactUtils::checkSpellbookIsNeeded(hero, assembleTo, artifactSlot))
|
if(ArtifactUtils::checkSpellbookIsNeeded(hero, assembleTo, artifactSlot))
|
||||||
giveHeroNewArtifact(hero, VLC->arth->objects[ArtifactID::SPELLBOOK], ArtifactPosition::SPELLBOOK);
|
giveHeroNewArtifact(hero, VLC->arth->objects[ArtifactID::SPELLBOOK], ArtifactPosition::SPELLBOOK);
|
||||||
|
|
||||||
AssembledArtifact aa;
|
AssembledArtifact aa;
|
||||||
aa.al = ArtifactLocation(hero, artifactSlot);
|
aa.al = dstLoc;
|
||||||
aa.builtArt = combinedArt;
|
aa.builtArt = combinedArt;
|
||||||
sendAndApply(&aa);
|
sendAndApply(&aa);
|
||||||
}
|
}
|
||||||
@ -2891,7 +2895,7 @@ bool CGameHandler::assembleArtifacts (ObjectInstanceID heroID, ArtifactPosition
|
|||||||
COMPLAIN_RET("assembleArtifacts: Artifact being attempted to disassemble but backpack is full!");
|
COMPLAIN_RET("assembleArtifacts: Artifact being attempted to disassemble but backpack is full!");
|
||||||
|
|
||||||
DisassembledArtifact da;
|
DisassembledArtifact da;
|
||||||
da.al = ArtifactLocation(hero, artifactSlot);
|
da.al = dstLoc;
|
||||||
sendAndApply(&da);
|
sendAndApply(&da);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3217,7 +3221,7 @@ void CGameHandler::handleTimeEvents()
|
|||||||
//prepare dialog
|
//prepare dialog
|
||||||
InfoWindow iw;
|
InfoWindow iw;
|
||||||
iw.player = color;
|
iw.player = color;
|
||||||
iw.text.appendRawString(ev.message);
|
iw.text = ev.message;
|
||||||
|
|
||||||
for (int i=0; i<ev.resources.size(); i++)
|
for (int i=0; i<ev.resources.size(); i++)
|
||||||
{
|
{
|
||||||
@ -3268,7 +3272,7 @@ void CGameHandler::handleTownEvents(CGTownInstance * town, NewTurn &n)
|
|||||||
// dialog
|
// dialog
|
||||||
InfoWindow iw;
|
InfoWindow iw;
|
||||||
iw.player = player;
|
iw.player = player;
|
||||||
iw.text.appendRawString(ev.message);
|
iw.text = ev.message;
|
||||||
|
|
||||||
if (ev.resources.nonZero())
|
if (ev.resources.nonZero())
|
||||||
{
|
{
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user