mirror of
https://github.com/vcmi/vcmi.git
synced 2025-08-15 20:03:15 +02:00
@@ -69,6 +69,7 @@
|
|||||||
"vcmi.lobby.noPreview" : "no preview",
|
"vcmi.lobby.noPreview" : "no preview",
|
||||||
"vcmi.lobby.noUnderground" : "no underground",
|
"vcmi.lobby.noUnderground" : "no underground",
|
||||||
|
|
||||||
|
"vcmi.client.errors.missingCampaigns" : "{Missing data files}\n\nCampaigns data files were not found! You may be using incomplete or corrupted Heroes 3 data files. Please reinstall game data.",
|
||||||
"vcmi.server.errors.existingProcess" : "Another VCMI server process is running. Please terminate it before starting a new game.",
|
"vcmi.server.errors.existingProcess" : "Another VCMI server process is running. Please terminate it before starting a new game.",
|
||||||
"vcmi.server.errors.modsToEnable" : "{Following mods are required}",
|
"vcmi.server.errors.modsToEnable" : "{Following mods are required}",
|
||||||
"vcmi.server.errors.modsToDisable" : "{Following mods must be disabled}",
|
"vcmi.server.errors.modsToDisable" : "{Following mods must be disabled}",
|
||||||
|
@@ -69,6 +69,7 @@
|
|||||||
"vcmi.lobby.noPreview" : "огляд недоступний",
|
"vcmi.lobby.noPreview" : "огляд недоступний",
|
||||||
"vcmi.lobby.noUnderground" : "немає підземелля",
|
"vcmi.lobby.noUnderground" : "немає підземелля",
|
||||||
|
|
||||||
|
"vcmi.client.errors.missingCampaigns" : "{Не вистачає файлів даних}\n\nФайли даних кампаній не знайдено! Можливо, ви використовуєте неповні або пошкоджені файли даних Heroes 3. Будь ласка, перевстановіть дані гри.",
|
||||||
"vcmi.server.errors.existingProcess" : "Працює інший процес vcmiserver, будь ласка, спочатку завершіть його",
|
"vcmi.server.errors.existingProcess" : "Працює інший процес vcmiserver, будь ласка, спочатку завершіть його",
|
||||||
"vcmi.server.errors.modsToEnable" : "{Потрібні модифікації для завантаження гри}",
|
"vcmi.server.errors.modsToEnable" : "{Потрібні модифікації для завантаження гри}",
|
||||||
"vcmi.server.errors.modsToDisable" : "{Модифікації що мають бути вимкнені}",
|
"vcmi.server.errors.modsToDisable" : "{Модифікації що мають бути вимкнені}",
|
||||||
|
@@ -10,7 +10,7 @@ android {
|
|||||||
applicationId "is.xyz.vcmi"
|
applicationId "is.xyz.vcmi"
|
||||||
minSdk 19
|
minSdk 19
|
||||||
targetSdk 33
|
targetSdk 33
|
||||||
versionCode 1410
|
versionCode 1412
|
||||||
versionName "1.4.1"
|
versionName "1.4.1"
|
||||||
setProperty("archivesBaseName", "vcmi")
|
setProperty("archivesBaseName", "vcmi")
|
||||||
}
|
}
|
||||||
|
@@ -257,10 +257,10 @@ int main(int argc, char * argv[])
|
|||||||
};
|
};
|
||||||
|
|
||||||
testFile("DATA/HELP.TXT", "VCMI requires Heroes III: Shadow of Death or Heroes III: Complete data files to run!");
|
testFile("DATA/HELP.TXT", "VCMI requires Heroes III: Shadow of Death or Heroes III: Complete data files to run!");
|
||||||
testFile("MODS/VCMI/MOD.JSON", "VCMI installation is corrupted! Built-in mod was not found!");
|
|
||||||
testFile("DATA/PLAYERS.PAL", "Heroes III data files are missing or corruped! Please reinstall them.");
|
|
||||||
testFile("SPRITES/DEFAULT.DEF", "Heroes III data files are missing or corruped! Please reinstall them.");
|
|
||||||
testFile("DATA/TENTCOLR.TXT", "Heroes III: Restoration of Erathia (including HD Edition) data files are not supported!");
|
testFile("DATA/TENTCOLR.TXT", "Heroes III: Restoration of Erathia (including HD Edition) data files are not supported!");
|
||||||
|
testFile("MODS/VCMI/MOD.JSON", "VCMI installation is corrupted! Built-in mod was not found!");
|
||||||
|
testFile("DATA/PLAYERS.PAL", "Heroes III data files (Data/H3Bitmap.lod) are incomplete or corruped! Please reinstall them.");
|
||||||
|
testFile("SPRITES/DEFAULT.DEF", "Heroes III data files (Data/H3Sprite.lod) are incomplete or corruped! Please reinstall them.");
|
||||||
|
|
||||||
srand ( (unsigned int)time(nullptr) );
|
srand ( (unsigned int)time(nullptr) );
|
||||||
|
|
||||||
@@ -487,7 +487,8 @@ static void quitApplication()
|
|||||||
vstd::clear_pointer(CSH);
|
vstd::clear_pointer(CSH);
|
||||||
vstd::clear_pointer(VLC);
|
vstd::clear_pointer(VLC);
|
||||||
|
|
||||||
vstd::clear_pointer(console);// should be removed after everything else since used by logging
|
// sometimes leads to a hang. TODO: investigate
|
||||||
|
//vstd::clear_pointer(console);// should be removed after everything else since used by logging
|
||||||
|
|
||||||
if(!settings["session"]["headless"].Bool())
|
if(!settings["session"]["headless"].Bool())
|
||||||
GH.screenHandler().close();
|
GH.screenHandler().close();
|
||||||
@@ -501,10 +502,16 @@ static void quitApplication()
|
|||||||
|
|
||||||
std::cout << "Ending...\n";
|
std::cout << "Ending...\n";
|
||||||
|
|
||||||
// this method is always called from event/network threads, which keep interface mutex locked
|
// Perform quick exit without executing static destructors and let OS cleanup anything that we did not
|
||||||
// unlock it here to avoid assertion failure on GH destruction in exit()
|
// We generally don't care about them and this leads to numerous issues, e.g.
|
||||||
GH.interfaceMutex.unlock();
|
// destruction of locked mutexes (fails an assertion), even in third-party libraries (as well as native libs on Android)
|
||||||
exit(0);
|
// Android - std::quick_exit is available only starting from API level 21
|
||||||
|
// Mingw, macOS and iOS - std::quick_exit is unavailable (at least in current version of CI)
|
||||||
|
#if (defined(__ANDROID_API__) && __ANDROID_API__ < 21) || (defined(__MINGW32__)) || defined(VCMI_APPLE)
|
||||||
|
::exit(0);
|
||||||
|
#else
|
||||||
|
std::quick_exit(0);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleQuit(bool ask)
|
void handleQuit(bool ask)
|
||||||
|
@@ -218,8 +218,7 @@ CHeroList::CEmptyHeroItem::CEmptyHeroItem()
|
|||||||
|
|
||||||
CHeroList::CHeroItem::CHeroItem(CHeroList *parent, const CGHeroInstance * Hero)
|
CHeroList::CHeroItem::CHeroItem(CHeroList *parent, const CGHeroInstance * Hero)
|
||||||
: CListItem(parent),
|
: CListItem(parent),
|
||||||
hero(Hero),
|
hero(Hero)
|
||||||
parentList(parent)
|
|
||||||
{
|
{
|
||||||
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
|
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
|
||||||
movement = std::make_shared<CAnimImage>(AnimationPath::builtin("IMOBIL"), 0, 0, 0, 1);
|
movement = std::make_shared<CAnimImage>(AnimationPath::builtin("IMOBIL"), 0, 0, 0, 1);
|
||||||
@@ -285,19 +284,17 @@ void CHeroList::CHeroItem::gesture(bool on, const Point & initialPosition, const
|
|||||||
const CGHeroInstance * heroLower = (heroPos > heroes.size() - 2) ? nullptr : heroes[heroPos + 1];
|
const CGHeroInstance * heroLower = (heroPos > heroes.size() - 2) ? nullptr : heroes[heroPos + 1];
|
||||||
|
|
||||||
std::vector<RadialMenuConfig> menuElements = {
|
std::vector<RadialMenuConfig> menuElements = {
|
||||||
{ RadialMenuConfig::ITEM_ALT_NN, heroUpper != nullptr, "altUpTop", "vcmi.radialWheel.moveTop", [this, heroPos]()
|
{ RadialMenuConfig::ITEM_ALT_NN, heroUpper != nullptr, "altUpTop", "vcmi.radialWheel.moveTop", [heroPos]()
|
||||||
{
|
{
|
||||||
for (int i = heroPos; i > 0; i--)
|
for (int i = heroPos; i > 0; i--)
|
||||||
LOCPLINT->localState->swapWanderingHero(i, i - 1);
|
LOCPLINT->localState->swapWanderingHero(i, i - 1);
|
||||||
parentList->updateWidget();
|
|
||||||
} },
|
} },
|
||||||
{ RadialMenuConfig::ITEM_ALT_NW, heroUpper != nullptr, "altUp", "vcmi.radialWheel.moveUp", [this, heroPos](){LOCPLINT->localState->swapWanderingHero(heroPos, heroPos - 1); parentList->updateWidget(); } },
|
{ RadialMenuConfig::ITEM_ALT_NW, heroUpper != nullptr, "altUp", "vcmi.radialWheel.moveUp", [heroPos](){LOCPLINT->localState->swapWanderingHero(heroPos, heroPos - 1); } },
|
||||||
{ RadialMenuConfig::ITEM_ALT_SW, heroLower != nullptr, "altDown", "vcmi.radialWheel.moveDown", [this, heroPos](){ LOCPLINT->localState->swapWanderingHero(heroPos, heroPos + 1); parentList->updateWidget(); } },
|
{ RadialMenuConfig::ITEM_ALT_SW, heroLower != nullptr, "altDown", "vcmi.radialWheel.moveDown", [heroPos](){ LOCPLINT->localState->swapWanderingHero(heroPos, heroPos + 1); } },
|
||||||
{ RadialMenuConfig::ITEM_ALT_SS, heroLower != nullptr, "altDownBottom", "vcmi.radialWheel.moveBottom", [this, heroPos, heroes]()
|
{ RadialMenuConfig::ITEM_ALT_SS, heroLower != nullptr, "altDownBottom", "vcmi.radialWheel.moveBottom", [heroPos, heroes]()
|
||||||
{
|
{
|
||||||
for (int i = heroPos; i < heroes.size() - 1; i++)
|
for (int i = heroPos; i < heroes.size() - 1; i++)
|
||||||
LOCPLINT->localState->swapWanderingHero(i, i + 1);
|
LOCPLINT->localState->swapWanderingHero(i, i + 1);
|
||||||
parentList->updateWidget();
|
|
||||||
} },
|
} },
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -365,8 +362,7 @@ std::shared_ptr<CIntObject> CTownList::createItem(size_t index)
|
|||||||
}
|
}
|
||||||
|
|
||||||
CTownList::CTownItem::CTownItem(CTownList *parent, const CGTownInstance *Town):
|
CTownList::CTownItem::CTownItem(CTownList *parent, const CGTownInstance *Town):
|
||||||
CListItem(parent),
|
CListItem(parent)
|
||||||
parentList(parent)
|
|
||||||
{
|
{
|
||||||
const std::vector<const CGTownInstance *> towns = LOCPLINT->localState->getOwnedTowns();
|
const std::vector<const CGTownInstance *> towns = LOCPLINT->localState->getOwnedTowns();
|
||||||
townIndex = std::distance(towns.begin(), std::find(towns.begin(), towns.end(), Town));
|
townIndex = std::distance(towns.begin(), std::find(towns.begin(), towns.end(), Town));
|
||||||
@@ -430,15 +426,13 @@ void CTownList::CTownItem::gesture(bool on, const Point & initialPosition, const
|
|||||||
{
|
{
|
||||||
for (int i = townIndex; i > 0; i--)
|
for (int i = townIndex; i > 0; i--)
|
||||||
LOCPLINT->localState->swapOwnedTowns(i, i - 1);
|
LOCPLINT->localState->swapOwnedTowns(i, i - 1);
|
||||||
parentList->updateWidget();
|
|
||||||
} },
|
} },
|
||||||
{ RadialMenuConfig::ITEM_ALT_NW, townUpperPos > -1, "altUp", "vcmi.radialWheel.moveUp", [this, townUpperPos](){LOCPLINT->localState->swapOwnedTowns(townIndex, townUpperPos); parentList->updateWidget(); } },
|
{ RadialMenuConfig::ITEM_ALT_NW, townUpperPos > -1, "altUp", "vcmi.radialWheel.moveUp", [this, townUpperPos](){LOCPLINT->localState->swapOwnedTowns(townIndex, townUpperPos); } },
|
||||||
{ RadialMenuConfig::ITEM_ALT_SW, townLowerPos > -1, "altDown", "vcmi.radialWheel.moveDown", [this, townLowerPos](){ LOCPLINT->localState->swapOwnedTowns(townIndex, townLowerPos); parentList->updateWidget(); } },
|
{ RadialMenuConfig::ITEM_ALT_SW, townLowerPos > -1, "altDown", "vcmi.radialWheel.moveDown", [this, townLowerPos](){ LOCPLINT->localState->swapOwnedTowns(townIndex, townLowerPos); } },
|
||||||
{ RadialMenuConfig::ITEM_ALT_SS, townLowerPos > -1, "altDownBottom", "vcmi.radialWheel.moveBottom", [this, towns]()
|
{ RadialMenuConfig::ITEM_ALT_SS, townLowerPos > -1, "altDownBottom", "vcmi.radialWheel.moveBottom", [this, towns]()
|
||||||
{
|
{
|
||||||
for (int i = townIndex; i < towns.size() - 1; i++)
|
for (int i = townIndex; i < towns.size() - 1; i++)
|
||||||
LOCPLINT->localState->swapOwnedTowns(i, i + 1);
|
LOCPLINT->localState->swapOwnedTowns(i, i + 1);
|
||||||
parentList->updateWidget();
|
|
||||||
} },
|
} },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -117,7 +117,6 @@ class CHeroList : public CList
|
|||||||
std::shared_ptr<CAnimImage> movement;
|
std::shared_ptr<CAnimImage> movement;
|
||||||
std::shared_ptr<CAnimImage> mana;
|
std::shared_ptr<CAnimImage> mana;
|
||||||
std::shared_ptr<CAnimImage> portrait;
|
std::shared_ptr<CAnimImage> portrait;
|
||||||
CHeroList *parentList;
|
|
||||||
public:
|
public:
|
||||||
const CGHeroInstance * const hero;
|
const CGHeroInstance * const hero;
|
||||||
|
|
||||||
@@ -152,7 +151,6 @@ class CTownList : public CList
|
|||||||
class CTownItem : public CListItem
|
class CTownItem : public CListItem
|
||||||
{
|
{
|
||||||
std::shared_ptr<CAnimImage> picture;
|
std::shared_ptr<CAnimImage> picture;
|
||||||
CTownList *parentList;
|
|
||||||
public:
|
public:
|
||||||
int townIndex;
|
int townIndex;
|
||||||
|
|
||||||
|
@@ -107,7 +107,8 @@ void BattleInterface::playIntroSoundAndUnlockInterface()
|
|||||||
{
|
{
|
||||||
auto onIntroPlayed = [this]()
|
auto onIntroPlayed = [this]()
|
||||||
{
|
{
|
||||||
if(LOCPLINT->battleInt)
|
// Make sure that battle have not ended while intro was playing AND that a different one has not started
|
||||||
|
if(LOCPLINT->battleInt.get() == this)
|
||||||
onIntroSoundPlayed();
|
onIntroSoundPlayed();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -503,7 +503,7 @@ void OptionsTab::SelectionWindow::recreate()
|
|||||||
int count = 0;
|
int count = 0;
|
||||||
for(auto & elem : allowedHeroes)
|
for(auto & elem : allowedHeroes)
|
||||||
{
|
{
|
||||||
CHero * type = VLC->heroh->objects[elem];
|
const CHero * type = elem.toHeroType();
|
||||||
if(type->heroClass->faction == selectedFaction)
|
if(type->heroClass->faction == selectedFaction)
|
||||||
{
|
{
|
||||||
count++;
|
count++;
|
||||||
|
@@ -382,12 +382,29 @@ void CMainMenu::openCampaignLobby(std::shared_ptr<CampaignState> campaign)
|
|||||||
|
|
||||||
void CMainMenu::openCampaignScreen(std::string name)
|
void CMainMenu::openCampaignScreen(std::string name)
|
||||||
{
|
{
|
||||||
if(vstd::contains(CMainMenuConfig::get().getCampaigns().Struct(), name))
|
auto const & config = CMainMenuConfig::get().getCampaigns();
|
||||||
|
|
||||||
|
if(!vstd::contains(config.Struct(), name))
|
||||||
{
|
{
|
||||||
GH.windows().createAndPushWindow<CCampaignScreen>(CMainMenuConfig::get().getCampaigns(), name);
|
logGlobal->error("Unknown campaign set: %s", name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logGlobal->error("Unknown campaign set: %s", name);
|
|
||||||
|
bool campaignsFound = true;
|
||||||
|
for (auto const & entry : config[name]["items"].Vector())
|
||||||
|
{
|
||||||
|
ResourcePath resourceID(entry["file"].String(), EResType::CAMPAIGN);
|
||||||
|
if (!CResourceHandler::get()->existsResource(resourceID))
|
||||||
|
campaignsFound = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!campaignsFound)
|
||||||
|
{
|
||||||
|
CInfoWindow::showInfoDialog(CGI->generaltexth->translate("vcmi.client.errors.missingCampaigns"), std::vector<std::shared_ptr<CComponent>>(), PlayerColor(1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GH.windows().createAndPushWindow<CCampaignScreen>(config, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMainMenu::startTutorial()
|
void CMainMenu::startTutorial()
|
||||||
|
@@ -118,11 +118,10 @@ void GameSettings::load(const JsonNode & input)
|
|||||||
|
|
||||||
const JsonNode & GameSettings::getValue(EGameSettings option) const
|
const JsonNode & GameSettings::getValue(EGameSettings option) const
|
||||||
{
|
{
|
||||||
assert(option < EGameSettings::OPTIONS_COUNT);
|
|
||||||
auto index = static_cast<size_t>(option);
|
auto index = static_cast<size_t>(option);
|
||||||
|
|
||||||
assert(!gameSettings[index].isNull());
|
assert(!gameSettings.at(index).isNull());
|
||||||
return gameSettings[index];
|
return gameSettings.at(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
@@ -97,11 +97,7 @@ CObjectClassesHandler::CObjectClassesHandler()
|
|||||||
#undef SET_HANDLER
|
#undef SET_HANDLER
|
||||||
}
|
}
|
||||||
|
|
||||||
CObjectClassesHandler::~CObjectClassesHandler()
|
CObjectClassesHandler::~CObjectClassesHandler() = default;
|
||||||
{
|
|
||||||
for(auto * p : objects)
|
|
||||||
delete p;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<JsonNode> CObjectClassesHandler::loadLegacyData()
|
std::vector<JsonNode> CObjectClassesHandler::loadLegacyData()
|
||||||
{
|
{
|
||||||
@@ -225,6 +221,9 @@ TObjectTypeHandler CObjectClassesHandler::loadSubObjectFromJson(const std::strin
|
|||||||
return createdObject;
|
return createdObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ObjectClass::ObjectClass() = default;
|
||||||
|
ObjectClass::~ObjectClass() = default;
|
||||||
|
|
||||||
std::string ObjectClass::getJsonKey() const
|
std::string ObjectClass::getJsonKey() const
|
||||||
{
|
{
|
||||||
return modScope + ':' + identifier;
|
return modScope + ':' + identifier;
|
||||||
@@ -240,9 +239,9 @@ std::string ObjectClass::getNameTranslated() const
|
|||||||
return VLC->generaltexth->translate(getNameTextID());
|
return VLC->generaltexth->translate(getNameTextID());
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectClass * CObjectClassesHandler::loadFromJson(const std::string & scope, const JsonNode & json, const std::string & name, size_t index)
|
std::unique_ptr<ObjectClass> CObjectClassesHandler::loadFromJson(const std::string & scope, const JsonNode & json, const std::string & name, size_t index)
|
||||||
{
|
{
|
||||||
auto * obj = new ObjectClass();
|
auto obj = std::make_unique<ObjectClass>();
|
||||||
|
|
||||||
obj->modScope = scope;
|
obj->modScope = scope;
|
||||||
obj->identifier = name;
|
obj->identifier = name;
|
||||||
@@ -263,31 +262,31 @@ ObjectClass * CObjectClassesHandler::loadFromJson(const std::string & scope, con
|
|||||||
if ( subMeta != "core")
|
if ( subMeta != "core")
|
||||||
logMod->warn("Object %s:%s.%s - attempt to load object with preset index! This option is reserved for built-in mod", subMeta, name, subData.first );
|
logMod->warn("Object %s:%s.%s - attempt to load object with preset index! This option is reserved for built-in mod", subMeta, name, subData.first );
|
||||||
size_t subIndex = subData.second["index"].Integer();
|
size_t subIndex = subData.second["index"].Integer();
|
||||||
loadSubObject(subData.second.meta, subData.first, subData.second, obj, subIndex);
|
loadSubObject(subData.second.meta, subData.first, subData.second, obj.get(), subIndex);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
loadSubObject(subData.second.meta, subData.first, subData.second, obj);
|
loadSubObject(subData.second.meta, subData.first, subData.second, obj.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj->id == MapObjectID::MONOLITH_TWO_WAY)
|
if (obj->id == MapObjectID::MONOLITH_TWO_WAY)
|
||||||
generateExtraMonolithsForRMG(obj);
|
generateExtraMonolithsForRMG(obj.get());
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CObjectClassesHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
|
void CObjectClassesHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
|
||||||
{
|
{
|
||||||
auto * object = loadFromJson(scope, data, name, objects.size());
|
objects.push_back(loadFromJson(scope, data, name, objects.size()));
|
||||||
objects.push_back(object);
|
|
||||||
VLC->identifiersHandler->registerObject(scope, "object", name, object->id);
|
VLC->identifiersHandler->registerObject(scope, "object", name, objects.back()->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CObjectClassesHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
|
void CObjectClassesHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
|
||||||
{
|
{
|
||||||
auto * object = loadFromJson(scope, data, name, index);
|
assert(objects[index] == nullptr); // ensure that this id was not loaded before
|
||||||
assert(objects[(si32)index] == nullptr); // ensure that this id was not loaded before
|
|
||||||
objects[static_cast<si32>(index)] = object;
|
objects[index] = loadFromJson(scope, data, name, index);
|
||||||
VLC->identifiersHandler->registerObject(scope, "object", name, object->id);
|
VLC->identifiersHandler->registerObject(scope, "object", name, objects[index]->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CObjectClassesHandler::loadSubObject(const std::string & identifier, JsonNode config, MapObjectID ID, MapObjectSubID subID)
|
void CObjectClassesHandler::loadSubObject(const std::string & identifier, JsonNode config, MapObjectID ID, MapObjectSubID subID)
|
||||||
@@ -299,7 +298,7 @@ void CObjectClassesHandler::loadSubObject(const std::string & identifier, JsonNo
|
|||||||
objects[ID.getNum()]->objects.resize(subID.getNum()+1);
|
objects[ID.getNum()]->objects.resize(subID.getNum()+1);
|
||||||
|
|
||||||
JsonUtils::inherit(config, objects.at(ID.getNum())->base);
|
JsonUtils::inherit(config, objects.at(ID.getNum())->base);
|
||||||
loadSubObject(config.meta, identifier, config, objects[ID.getNum()], subID.getNum());
|
loadSubObject(config.meta, identifier, config, objects[ID.getNum()].get(), subID.getNum());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CObjectClassesHandler::removeSubObject(MapObjectID ID, MapObjectSubID subID)
|
void CObjectClassesHandler::removeSubObject(MapObjectID ID, MapObjectSubID subID)
|
||||||
@@ -335,7 +334,7 @@ TObjectTypeHandler CObjectClassesHandler::getHandlerFor(const std::string & scop
|
|||||||
std::optional<si32> id = VLC->identifiers()->getIdentifier(scope, "object", type);
|
std::optional<si32> id = VLC->identifiers()->getIdentifier(scope, "object", type);
|
||||||
if(id)
|
if(id)
|
||||||
{
|
{
|
||||||
auto * object = objects[id.value()];
|
const auto & object = objects[id.value()];
|
||||||
std::optional<si32> subID = VLC->identifiers()->getIdentifier(scope, object->getJsonKey(), subtype);
|
std::optional<si32> subID = VLC->identifiers()->getIdentifier(scope, object->getJsonKey(), subtype);
|
||||||
|
|
||||||
if (subID)
|
if (subID)
|
||||||
@@ -356,7 +355,7 @@ std::set<MapObjectID> CObjectClassesHandler::knownObjects() const
|
|||||||
{
|
{
|
||||||
std::set<MapObjectID> ret;
|
std::set<MapObjectID> ret;
|
||||||
|
|
||||||
for(auto * entry : objects)
|
for(auto & entry : objects)
|
||||||
if (entry)
|
if (entry)
|
||||||
ret.insert(entry->id);
|
ret.insert(entry->id);
|
||||||
|
|
||||||
@@ -406,7 +405,7 @@ void CObjectClassesHandler::beforeValidate(JsonNode & object)
|
|||||||
|
|
||||||
void CObjectClassesHandler::afterLoadFinalization()
|
void CObjectClassesHandler::afterLoadFinalization()
|
||||||
{
|
{
|
||||||
for(auto * entry : objects)
|
for(auto & entry : objects)
|
||||||
{
|
{
|
||||||
if (!entry)
|
if (!entry)
|
||||||
continue;
|
continue;
|
||||||
|
@@ -45,7 +45,7 @@ class CGObjectInstance;
|
|||||||
using TObjectTypeHandler = std::shared_ptr<AObjectTypeHandler>;
|
using TObjectTypeHandler = std::shared_ptr<AObjectTypeHandler>;
|
||||||
|
|
||||||
/// Class responsible for creation of adventure map objects of specific type
|
/// Class responsible for creation of adventure map objects of specific type
|
||||||
class DLL_LINKAGE ObjectClass
|
class DLL_LINKAGE ObjectClass : boost::noncopyable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::string modScope;
|
std::string modScope;
|
||||||
@@ -57,7 +57,8 @@ public:
|
|||||||
JsonNode base;
|
JsonNode base;
|
||||||
std::vector<TObjectTypeHandler> objects;
|
std::vector<TObjectTypeHandler> objects;
|
||||||
|
|
||||||
ObjectClass() = default;
|
ObjectClass();
|
||||||
|
~ObjectClass();
|
||||||
|
|
||||||
std::string getJsonKey() const;
|
std::string getJsonKey() const;
|
||||||
std::string getNameTextID() const;
|
std::string getNameTextID() const;
|
||||||
@@ -65,10 +66,10 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Main class responsible for creation of all adventure map objects
|
/// Main class responsible for creation of all adventure map objects
|
||||||
class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase
|
class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase, boost::noncopyable
|
||||||
{
|
{
|
||||||
/// list of object handlers, each of them handles only one type
|
/// list of object handlers, each of them handles only one type
|
||||||
std::vector<ObjectClass * > objects;
|
std::vector< std::unique_ptr<ObjectClass> > objects;
|
||||||
|
|
||||||
/// map that is filled during contruction with all known handlers. Not serializeable due to usage of std::function
|
/// map that is filled during contruction with all known handlers. Not serializeable due to usage of std::function
|
||||||
std::map<std::string, std::function<TObjectTypeHandler()> > handlerConstructors;
|
std::map<std::string, std::function<TObjectTypeHandler()> > handlerConstructors;
|
||||||
@@ -82,7 +83,7 @@ class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase
|
|||||||
void loadSubObject(const std::string & scope, const std::string & identifier, const JsonNode & entry, ObjectClass * obj);
|
void loadSubObject(const std::string & scope, const std::string & identifier, const JsonNode & entry, ObjectClass * obj);
|
||||||
void loadSubObject(const std::string & scope, const std::string & identifier, const JsonNode & entry, ObjectClass * obj, size_t index);
|
void loadSubObject(const std::string & scope, const std::string & identifier, const JsonNode & entry, ObjectClass * obj, size_t index);
|
||||||
|
|
||||||
ObjectClass * loadFromJson(const std::string & scope, const JsonNode & json, const std::string & name, size_t index);
|
std::unique_ptr<ObjectClass> loadFromJson(const std::string & scope, const JsonNode & json, const std::string & name, size_t index);
|
||||||
|
|
||||||
void generateExtraMonolithsForRMG(ObjectClass * container);
|
void generateExtraMonolithsForRMG(ObjectClass * container);
|
||||||
|
|
||||||
|
@@ -77,7 +77,7 @@ void CTownInstanceConstructor::afterLoadFinalization()
|
|||||||
{
|
{
|
||||||
filters[entry.first] = LogicalExpression<BuildingID>(entry.second, [this](const JsonNode & node)
|
filters[entry.first] = LogicalExpression<BuildingID>(entry.second, [this](const JsonNode & node)
|
||||||
{
|
{
|
||||||
return BuildingID(VLC->identifiers()->getIdentifier("building." + faction->getJsonKey(), node.Vector()[0]).value());
|
return BuildingID(VLC->identifiers()->getIdentifier("building." + faction->getJsonKey(), node.Vector()[0]).value_or(-1));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
#include "../CGeneralTextHandler.h"
|
#include "../CGeneralTextHandler.h"
|
||||||
#include "../gameState/CGameState.h"
|
#include "../gameState/CGameState.h"
|
||||||
#include "../CPlayerState.h"
|
#include "../CPlayerState.h"
|
||||||
|
#include "../MetaString.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
@@ -110,7 +111,12 @@ void CArmedInstance::updateMoraleBonusFromArmy()
|
|||||||
else if (!factions.empty()) // no bonus from empty garrison
|
else if (!factions.empty()) // no bonus from empty garrison
|
||||||
{
|
{
|
||||||
b->val = 2 - static_cast<si32>(factionsInArmy);
|
b->val = 2 - static_cast<si32>(factionsInArmy);
|
||||||
description = boost::str(boost::format(VLC->generaltexth->arraytxt[114]) % factionsInArmy % b->val); //Troops of %d alignments %d
|
MetaString formatter;
|
||||||
|
formatter.appendTextID("core.arraytxt.114"); //Troops of %d alignments %d
|
||||||
|
formatter.replaceNumber(factionsInArmy);
|
||||||
|
formatter.replaceNumber(b->val);
|
||||||
|
|
||||||
|
description = formatter.toString();
|
||||||
description = description.substr(0, description.size()-3);//trim value
|
description = description.substr(0, description.size()-3);//trim value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -160,7 +160,7 @@ void CGDwelling::pickRandomObject(CRandomGenerator & rand)
|
|||||||
if (subID == MapObjectSubID())
|
if (subID == MapObjectSubID())
|
||||||
{
|
{
|
||||||
logGlobal->error("Error: failed to find dwelling for %s of level %d", (*VLC->townh)[faction]->getNameTranslated(), int(level));
|
logGlobal->error("Error: failed to find dwelling for %s of level %d", (*VLC->townh)[faction]->getNameTranslated(), int(level));
|
||||||
ID = Obj::CREATURE_GENERATOR4;
|
ID = Obj::CREATURE_GENERATOR1;
|
||||||
subID = *RandomGeneratorUtil::nextItem(VLC->objtypeh->knownSubObjects(Obj::CREATURE_GENERATOR1), rand);
|
subID = *RandomGeneratorUtil::nextItem(VLC->objtypeh->knownSubObjects(Obj::CREATURE_GENERATOR1), rand);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1145,42 +1145,47 @@ CGObjectInstance * CMapLoaderH3M::readWitchHut(const int3 & position, std::share
|
|||||||
auto * object = readGeneric(position, objectTemplate);
|
auto * object = readGeneric(position, objectTemplate);
|
||||||
auto * rewardable = dynamic_cast<CRewardableObject*>(object);
|
auto * rewardable = dynamic_cast<CRewardableObject*>(object);
|
||||||
|
|
||||||
assert(rewardable);
|
|
||||||
|
|
||||||
// AB and later maps have allowed abilities defined in H3M
|
// AB and later maps have allowed abilities defined in H3M
|
||||||
if(features.levelAB)
|
if(features.levelAB)
|
||||||
{
|
{
|
||||||
std::set<SecondarySkill> allowedAbilities;
|
std::set<SecondarySkill> allowedAbilities;
|
||||||
reader->readBitmaskSkills(allowedAbilities, false);
|
reader->readBitmaskSkills(allowedAbilities, false);
|
||||||
|
|
||||||
if(allowedAbilities.size() != 1)
|
if (rewardable)
|
||||||
{
|
{
|
||||||
auto defaultAllowed = VLC->skillh->getDefaultAllowed();
|
if(allowedAbilities.size() != 1)
|
||||||
|
{
|
||||||
|
auto defaultAllowed = VLC->skillh->getDefaultAllowed();
|
||||||
|
|
||||||
for(int skillID = features.skillsCount; skillID < defaultAllowed.size(); ++skillID)
|
for(int skillID = features.skillsCount; skillID < defaultAllowed.size(); ++skillID)
|
||||||
if(defaultAllowed.count(skillID))
|
if(defaultAllowed.count(skillID))
|
||||||
allowedAbilities.insert(SecondarySkill(skillID));
|
allowedAbilities.insert(SecondarySkill(skillID));
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonNode variable;
|
JsonNode variable;
|
||||||
if (allowedAbilities.size() == 1)
|
if (allowedAbilities.size() == 1)
|
||||||
{
|
{
|
||||||
variable.String() = VLC->skills()->getById(*allowedAbilities.begin())->getJsonKey();
|
variable.String() = VLC->skills()->getById(*allowedAbilities.begin())->getJsonKey();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JsonVector anyOfList;
|
||||||
|
for (auto const & skill : allowedAbilities)
|
||||||
|
{
|
||||||
|
JsonNode entry;
|
||||||
|
entry.String() = VLC->skills()->getById(skill)->getJsonKey();
|
||||||
|
anyOfList.push_back(entry);
|
||||||
|
}
|
||||||
|
variable["anyOf"].Vector() = anyOfList;
|
||||||
|
}
|
||||||
|
|
||||||
|
variable.setMeta(ModScope::scopeGame()); // list may include skills from all mods
|
||||||
|
rewardable->configuration.presetVariable("secondarySkill", "gainedSkill", variable);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
JsonVector anyOfList;
|
logGlobal->warn("Failed to set allowed secondary skills to a Witch Hut! Object is not rewardable!");
|
||||||
for (auto const & skill : allowedAbilities)
|
|
||||||
{
|
|
||||||
JsonNode entry;
|
|
||||||
entry.String() = VLC->skills()->getById(skill)->getJsonKey();
|
|
||||||
anyOfList.push_back(entry);
|
|
||||||
}
|
|
||||||
variable["anyOf"].Vector() = anyOfList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
variable.setMeta(ModScope::scopeGame()); // list may include skills from all mods
|
|
||||||
rewardable->configuration.presetVariable("secondarySkill", "gainedSkill", variable);
|
|
||||||
}
|
}
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
@@ -1201,45 +1206,52 @@ CGObjectInstance * CMapLoaderH3M::readScholar(const int3 & position, std::shared
|
|||||||
auto bonusType = static_cast<ScholarBonusType>(bonusTypeRaw);
|
auto bonusType = static_cast<ScholarBonusType>(bonusTypeRaw);
|
||||||
auto bonusID = reader->readUInt8();
|
auto bonusID = reader->readUInt8();
|
||||||
|
|
||||||
switch (bonusType)
|
if (rewardable)
|
||||||
{
|
{
|
||||||
case ScholarBonusType::PRIM_SKILL:
|
switch (bonusType)
|
||||||
{
|
{
|
||||||
JsonNode variable;
|
case ScholarBonusType::PRIM_SKILL:
|
||||||
JsonNode dice;
|
{
|
||||||
variable.String() = NPrimarySkill::names[bonusID];
|
JsonNode variable;
|
||||||
variable.setMeta(ModScope::scopeGame());
|
JsonNode dice;
|
||||||
dice.Integer() = 80;
|
variable.String() = NPrimarySkill::names[bonusID];
|
||||||
rewardable->configuration.presetVariable("primarySkill", "gainedStat", variable);
|
variable.setMeta(ModScope::scopeGame());
|
||||||
rewardable->configuration.presetVariable("dice", "0", dice);
|
dice.Integer() = 80;
|
||||||
break;
|
rewardable->configuration.presetVariable("primarySkill", "gainedStat", variable);
|
||||||
|
rewardable->configuration.presetVariable("dice", "0", dice);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ScholarBonusType::SECONDARY_SKILL:
|
||||||
|
{
|
||||||
|
JsonNode variable;
|
||||||
|
JsonNode dice;
|
||||||
|
variable.String() = VLC->skills()->getByIndex(bonusID)->getJsonKey();
|
||||||
|
variable.setMeta(ModScope::scopeGame());
|
||||||
|
dice.Integer() = 50;
|
||||||
|
rewardable->configuration.presetVariable("secondarySkill", "gainedSkill", variable);
|
||||||
|
rewardable->configuration.presetVariable("dice", "0", dice);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ScholarBonusType::SPELL:
|
||||||
|
{
|
||||||
|
JsonNode variable;
|
||||||
|
JsonNode dice;
|
||||||
|
variable.String() = VLC->spells()->getByIndex(bonusID)->getJsonKey();
|
||||||
|
variable.setMeta(ModScope::scopeGame());
|
||||||
|
dice.Integer() = 20;
|
||||||
|
rewardable->configuration.presetVariable("spell", "gainedSpell", variable);
|
||||||
|
rewardable->configuration.presetVariable("dice", "0", dice);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ScholarBonusType::RANDOM:
|
||||||
|
break;// No-op
|
||||||
|
default:
|
||||||
|
logGlobal->warn("Map '%s': Invalid Scholar settings! Ignoring...", mapName);
|
||||||
}
|
}
|
||||||
case ScholarBonusType::SECONDARY_SKILL:
|
}
|
||||||
{
|
else
|
||||||
JsonNode variable;
|
{
|
||||||
JsonNode dice;
|
logGlobal->warn("Failed to set reward parameters for a Scholar! Object is not rewardable!");
|
||||||
variable.String() = VLC->skills()->getByIndex(bonusID)->getJsonKey();
|
|
||||||
variable.setMeta(ModScope::scopeGame());
|
|
||||||
dice.Integer() = 50;
|
|
||||||
rewardable->configuration.presetVariable("secondarySkill", "gainedSkill", variable);
|
|
||||||
rewardable->configuration.presetVariable("dice", "0", dice);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ScholarBonusType::SPELL:
|
|
||||||
{
|
|
||||||
JsonNode variable;
|
|
||||||
JsonNode dice;
|
|
||||||
variable.String() = VLC->spells()->getByIndex(bonusID)->getJsonKey();
|
|
||||||
variable.setMeta(ModScope::scopeGame());
|
|
||||||
dice.Integer() = 20;
|
|
||||||
rewardable->configuration.presetVariable("spell", "gainedSpell", variable);
|
|
||||||
rewardable->configuration.presetVariable("dice", "0", dice);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ScholarBonusType::RANDOM:
|
|
||||||
break;// No-op
|
|
||||||
default:
|
|
||||||
logGlobal->warn("Map '%s': Invalid Scholar settings! Ignoring...", mapName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reader->skipZero(6);
|
reader->skipZero(6);
|
||||||
@@ -1362,16 +1374,21 @@ CGObjectInstance * CMapLoaderH3M::readShrine(const int3 & position, std::shared_
|
|||||||
auto * object = readGeneric(position, objectTemplate);
|
auto * object = readGeneric(position, objectTemplate);
|
||||||
auto * rewardable = dynamic_cast<CRewardableObject*>(object);
|
auto * rewardable = dynamic_cast<CRewardableObject*>(object);
|
||||||
|
|
||||||
assert(rewardable);
|
|
||||||
|
|
||||||
SpellID spell = reader->readSpell32();
|
SpellID spell = reader->readSpell32();
|
||||||
|
|
||||||
if(spell != SpellID::NONE)
|
if (rewardable)
|
||||||
{
|
{
|
||||||
JsonNode variable;
|
if(spell != SpellID::NONE)
|
||||||
variable.String() = VLC->spells()->getById(spell)->getJsonKey();
|
{
|
||||||
variable.setMeta(ModScope::scopeGame()); // list may include spells from all mods
|
JsonNode variable;
|
||||||
rewardable->configuration.presetVariable("spell", "gainedSpell", variable);
|
variable.String() = VLC->spells()->getById(spell)->getJsonKey();
|
||||||
|
variable.setMeta(ModScope::scopeGame()); // list may include spells from all mods
|
||||||
|
rewardable->configuration.presetVariable("spell", "gainedSpell", variable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logGlobal->warn("Failed to set selected spell to a Shrine!. Object is not rewardable!");
|
||||||
}
|
}
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
@@ -196,7 +196,10 @@ void TurnOrderProcessor::doStartNewDay()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!activePlayer)
|
if(!activePlayer)
|
||||||
|
{
|
||||||
gameHandler->gameLobby()->setState(EServerState::GAMEPLAY_ENDED);
|
gameHandler->gameLobby()->setState(EServerState::GAMEPLAY_ENDED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::swap(actedPlayers, awaitingPlayers);
|
std::swap(actedPlayers, awaitingPlayers);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user