From 2960895041c4789b99134a44021118e0893b573d Mon Sep 17 00:00:00 2001 From: nordsoft Date: Sun, 17 Sep 2023 22:19:45 +0200 Subject: [PATCH] Issues fixed --- lib/CCreatureSet.cpp | 5 +- lib/CCreatureSet.h | 7 ++- lib/mapObjects/CArmedInstance.cpp | 6 +++ lib/mapObjects/CArmedInstance.h | 3 ++ lib/mapObjects/CGHeroInstance.cpp | 5 +- lib/mapObjects/CGPandoraBox.cpp | 32 ++++++++++-- lib/mapObjects/CGTownInstance.cpp | 7 ++- lib/mapObjects/CQuest.cpp | 2 +- lib/mapObjects/CQuest.h | 2 +- lib/mapObjects/MiscObjects.cpp | 12 +++-- lib/rewardable/Limiter.cpp | 52 +++---------------- lib/rewardable/Reward.cpp | 74 +++++---------------------- lib/serializer/JsonSerializeFormat.h | 32 ++++++++++-- mapeditor/inspector/rewardswidget.cpp | 10 +++- 14 files changed, 117 insertions(+), 132 deletions(-) diff --git a/lib/CCreatureSet.cpp b/lib/CCreatureSet.cpp index ec09548c0..5bc12977f 100644 --- a/lib/CCreatureSet.cpp +++ b/lib/CCreatureSet.cpp @@ -624,12 +624,13 @@ void CCreatureSet::armyChanged() } -void CCreatureSet::serializeJson(JsonSerializeFormat & handler, const std::string & fieldName, const std::optional fixedSize) +void CCreatureSet::serializeJson(JsonSerializeFormat & handler, const std::string & armyFieldName, const std::optional fixedSize) { if(handler.saving && stacks.empty()) return; - auto a = handler.enterArray(fieldName); + handler.serializeEnum("formation", formation, NArmyFormation::names); + auto a = handler.enterArray(armyFieldName); if(handler.saving) diff --git a/lib/CCreatureSet.h b/lib/CCreatureSet.h index e72d0eb14..2c4944f05 100644 --- a/lib/CCreatureSet.h +++ b/lib/CCreatureSet.h @@ -206,6 +206,11 @@ enum class EArmyFormation : uint8_t TIGHT }; +namespace NArmyFormation +{ + static const std::vector names{ "wide", "tight" }; +} + class DLL_LINKAGE CCreatureSet : public IArmyDescriptor //seven combined creatures { CCreatureSet(const CCreatureSet &) = delete; @@ -284,7 +289,7 @@ public: h & formation; } - void serializeJson(JsonSerializeFormat & handler, const std::string & fieldName, const std::optional fixedSize = std::nullopt); + void serializeJson(JsonSerializeFormat & handler, const std::string & armyFieldName, const std::optional fixedSize = std::nullopt); operator bool() const { diff --git a/lib/mapObjects/CArmedInstance.cpp b/lib/mapObjects/CArmedInstance.cpp index 37f7fd950..bc13a206d 100644 --- a/lib/mapObjects/CArmedInstance.cpp +++ b/lib/mapObjects/CArmedInstance.cpp @@ -160,4 +160,10 @@ const IBonusBearer* CArmedInstance::getBonusBearer() const return this; } +void CArmedInstance::serializeJsonOptions(JsonSerializeFormat & handler) +{ + CGObjectInstance::serializeJsonOptions(handler); + CCreatureSet::serializeJson(handler, "army", 7); +} + VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/CArmedInstance.h b/lib/mapObjects/CArmedInstance.h index e14447c55..378dcf96c 100644 --- a/lib/mapObjects/CArmedInstance.h +++ b/lib/mapObjects/CArmedInstance.h @@ -18,6 +18,7 @@ VCMI_LIB_NAMESPACE_BEGIN class BattleInfo; class CGameState; +class JsonSerializeFormat; class DLL_LINKAGE CArmedInstance: public CGObjectInstance, public CBonusSystemNode, public CCreatureSet, public IConstBonusProvider { @@ -48,6 +49,8 @@ public: { return this->tempOwner; } + + void serializeJsonOptions(JsonSerializeFormat & handler) override; template void serialize(Handler &h, const int version) { diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index e8cde5e39..569ae1701 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -1709,10 +1709,7 @@ void CGHeroInstance::serializeJsonOptions(JsonSerializeFormat & handler) setHeroTypeName(typeName); } - static const std::vector FORMATIONS = { "wide", "tight" }; - - CCreatureSet::serializeJson(handler, "army", 7); - handler.serializeEnum("formation", formation, FORMATIONS); + CArmedInstance::serializeJsonOptions(handler); { static constexpr int NO_PATROLING = -1; diff --git a/lib/mapObjects/CGPandoraBox.cpp b/lib/mapObjects/CGPandoraBox.cpp index b7538195a..48910fa20 100644 --- a/lib/mapObjects/CGPandoraBox.cpp +++ b/lib/mapObjects/CGPandoraBox.cpp @@ -28,9 +28,15 @@ VCMI_LIB_NAMESPACE_BEGIN void CGPandoraBox::init() { blockVisit = true; + configuration.info.emplace_back(); + configuration.info.back().visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; for(auto & i : configuration.info) + { i.reward.removeObject = true; + if(!message.empty() && i.message.empty()) + i.message = MetaString::createFromRawString(message); + } } void CGPandoraBox::initObj(CRandomGenerator & rand) @@ -202,14 +208,17 @@ void CGPandoraBox::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answe void CGPandoraBox::serializeJsonOptions(JsonSerializeFormat & handler) { CRewardableObject::serializeJsonOptions(handler); + handler.serializeString("guardMessage", message); if(!handler.saving) { - //backward compatibility - CCreatureSet::serializeJson(handler, "guards", 7); - configuration.info.emplace_back(); - Rewardable::VisitInfo & vinfo = configuration.info.back(); + //backward compatibility for VCMI maps that use old Pandora Box format + if(!handler.getCurrent()["guards"].Vector().empty()) + CCreatureSet::serializeJson(handler, "guards", 7); + + bool hasSomething = false; + Rewardable::VisitInfo vinfo; vinfo.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; handler.serializeInt("experience", vinfo.reward.heroExperience, 0); @@ -230,6 +239,8 @@ void CGPandoraBox::serializeJsonOptions(JsonSerializeFormat & handler) for(int idx = 0; idx < vinfo.reward.primary.size(); idx ++) { handler.serializeInt(NPrimarySkill::names[idx], vinfo.reward.primary[idx], 0); + if(vinfo.reward.primary[idx]) + hasSomething = true; } } @@ -261,6 +272,19 @@ void CGPandoraBox::serializeJsonOptions(JsonSerializeFormat & handler) vinfo.reward.secondary[rawId] = level; } } + + hasSomething = hasSomething + || vinfo.reward.heroExperience + || vinfo.reward.manaDiff + || vinfo.reward.resources.nonZero() + || !vinfo.reward.bonuses.empty() + || !vinfo.reward.artifacts.empty() + || !vinfo.reward.secondary.empty() + || !vinfo.reward.artifacts.empty() + || !vinfo.reward.creatures.empty(); + + if(hasSomething) + configuration.info.push_back(vinfo); } } diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index 9363a29cb..b82fe8d68 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -1093,11 +1093,10 @@ void CGTownInstance::reset() void CGTownInstance::serializeJsonOptions(JsonSerializeFormat & handler) { - static const std::vector FORMATIONS = { "wide", "tight" }; - CGObjectInstance::serializeJsonOwner(handler); - CCreatureSet::serializeJson(handler, "army", 7); - handler.serializeEnum("tightFormation", formation, FORMATIONS); + if(!handler.saving) + handler.serializeEnum("tightFormation", formation, NArmyFormation::names); //for old format + CArmedInstance::serializeJsonOptions(handler); handler.serializeString("name", name); { diff --git a/lib/mapObjects/CQuest.cpp b/lib/mapObjects/CQuest.cpp index 3dae6194b..13872ea81 100644 --- a/lib/mapObjects/CQuest.cpp +++ b/lib/mapObjects/CQuest.cpp @@ -799,7 +799,7 @@ void CGSeerHut::serializeJsonOptions(JsonSerializeFormat & handler) if(!handler.saving) { - //backward compatibility + //backward compatibility for VCMI maps that use old SeerHut format auto s = handler.enterStruct("reward"); const JsonNode & rewardsJson = handler.getCurrent(); diff --git a/lib/mapObjects/CQuest.h b/lib/mapObjects/CQuest.h index 7cb0df2bc..3885229dd 100644 --- a/lib/mapObjects/CQuest.h +++ b/lib/mapObjects/CQuest.h @@ -138,7 +138,7 @@ protected: void afterAddToMapCommon(CMap * map) const; }; -class DLL_LINKAGE CGSeerHut : public CRewardableObject, public IQuestObject //army is used when giving reward +class DLL_LINKAGE CGSeerHut : public CRewardableObject, public IQuestObject { public: std::string seerName; diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 24c32a4e4..fe2b70f5e 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -196,7 +196,7 @@ void CGMine::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) con void CGMine::serializeJsonOptions(JsonSerializeFormat & handler) { - CCreatureSet::serializeJson(handler, "army", 7); + CArmedInstance::serializeJsonOptions(handler); if(isAbandoned()) { @@ -316,7 +316,9 @@ void CGResource::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) void CGResource::serializeJsonOptions(JsonSerializeFormat & handler) { - CCreatureSet::serializeJson(handler, "guards", 7); + CArmedInstance::serializeJsonOptions(handler); + if(!handler.saving && !handler.getCurrent()["guards"].Vector().empty()) + CCreatureSet::serializeJson(handler, "guards", 7); handler.serializeInt("amount", amount, 0); handler.serializeString("guardMessage", message); } @@ -827,7 +829,9 @@ void CGArtifact::afterAddToMap(CMap * map) void CGArtifact::serializeJsonOptions(JsonSerializeFormat& handler) { handler.serializeString("guardMessage", message); - CCreatureSet::serializeJson(handler, "guards" ,7); + CArmedInstance::serializeJsonOptions(handler); + if(!handler.saving && !handler.getCurrent()["guards"].Vector().empty()) + CCreatureSet::serializeJson(handler, "guards", 7); if(handler.saving && ID == Obj::SPELL_SCROLL) { @@ -1233,7 +1237,7 @@ void CGGarrison::serializeJsonOptions(JsonSerializeFormat& handler) { handler.serializeBool("removableUnits", removableUnits); serializeJsonOwner(handler); - CCreatureSet::serializeJson(handler, "army", 7); + CArmedInstance::serializeJsonOptions(handler); } void CGMagi::reset() diff --git a/lib/rewardable/Limiter.cpp b/lib/rewardable/Limiter.cpp index 9d837ccb8..bf3fb2911 100644 --- a/lib/rewardable/Limiter.cpp +++ b/lib/rewardable/Limiter.cpp @@ -133,56 +133,18 @@ void Rewardable::Limiter::serializeJson(JsonSerializeFormat & handler) handler.serializeInt("manaPoints", manaPoints); handler.serializeIdArray("artifacts", artifacts); handler.enterArray("creatures").serializeStruct(creatures); - { - auto a = handler.enterArray("primary"); - a.syncSize(primary); - for(int i = 0; i < primary.size(); ++i) - a.serializeInt(i, primary[i]); - } - + handler.enterArray("primary").serializeArray(primary); { auto a = handler.enterArray("secondary"); - std::vector> fieldValue; - if(handler.saving) + std::vector> fieldValue(secondary.begin(), secondary.end()); + a.serializeStruct>(fieldValue, [](JsonSerializeFormat & h, std::pair & e) { - for(auto & i : secondary) - { - auto key = VLC->skillh->encodeSkill(i.first); - auto value = NSecondarySkill::levels.at(i.second); - fieldValue.emplace_back(key, value); - } - } + h.serializeId("skill", e.first, SecondarySkill{}, VLC->skillh->decodeSkill, VLC->skillh->encodeSkill); + h.serializeId("level", e.second, 0, [](const std::string & i){return vstd::find_pos(NSecondarySkill::levels, i);}, [](si32 i){return NSecondarySkill::levels.at(i);}); + }); a.syncSize(fieldValue); - for(int i = 0; i < fieldValue.size(); ++i) - { - auto e = a.enterStruct(i); - e->serializeString("skill", fieldValue[i].first); - e->serializeString("level", fieldValue[i].second); - } - if(!handler.saving) - { - for(auto & i : fieldValue) - { - const int skillId = VLC->skillh->decodeSkill(i.first); - if(skillId < 0) - { - logGlobal->error("Invalid secondary skill %s", i.first); - continue; - } - - const int level = vstd::find_pos(NSecondarySkill::levels, i.second); - if(level < 0) - { - logGlobal->error("Invalid secondary skill level%s", i.second); - continue; - } - - secondary[SecondarySkill(skillId)] = level; - } - - } + secondary = std::map(fieldValue.begin(), fieldValue.end()); } - //sublimiters auto serializeSublimitersList = [&handler](const std::string & field, LimitersList & container) { diff --git a/lib/rewardable/Reward.cpp b/lib/rewardable/Reward.cpp index 07da516cd..bbdbf3cdc 100644 --- a/lib/rewardable/Reward.cpp +++ b/lib/rewardable/Reward.cpp @@ -119,76 +119,28 @@ void Rewardable::Reward::serializeJson(JsonSerializeFormat & handler) handler.serializeIdArray("artifacts", artifacts); handler.serializeIdArray("spells", spells); handler.enterArray("creatures").serializeStruct(creatures); - { - auto a = handler.enterArray("primary"); - a.syncSize(primary); - for(int i = 0; i < primary.size(); ++i) - a.serializeInt(i, primary[i]); - } - + handler.enterArray("primary").serializeArray(primary); { auto a = handler.enterArray("secondary"); - std::vector> fieldValue; - if(handler.saving) + std::vector> fieldValue(secondary.begin(), secondary.end()); + a.serializeStruct>(fieldValue, [](JsonSerializeFormat & h, std::pair & e) { - for(auto & i : secondary) - { - auto key = VLC->skillh->encodeSkill(i.first); - auto value = NSecondarySkill::levels.at(i.second); - fieldValue.emplace_back(key, value); - } - } + h.serializeId("skill", e.first, SecondarySkill{}, VLC->skillh->decodeSkill, VLC->skillh->encodeSkill); + h.serializeId("level", e.second, 0, [](const std::string & i){return vstd::find_pos(NSecondarySkill::levels, i);}, [](si32 i){return NSecondarySkill::levels.at(i);}); + }); a.syncSize(fieldValue); - for(int i = 0; i < fieldValue.size(); ++i) - { - auto e = a.enterStruct(i); - e->serializeString("skill", fieldValue[i].first); - e->serializeString("level", fieldValue[i].second); - } - if(!handler.saving) - { - for(auto & i : fieldValue) - { - const int skillId = VLC->skillh->decodeSkill(i.first); - if(skillId < 0) - { - logGlobal->error("Invalid secondary skill %s", i.first); - continue; - } - - const int level = vstd::find_pos(NSecondarySkill::levels, i.second); - if(level < 0) - { - logGlobal->error("Invalid secondary skill level%s", i.second); - continue; - } - - secondary[SecondarySkill(skillId)] = level; - } - - } + secondary = std::map(fieldValue.begin(), fieldValue.end()); } { auto a = handler.enterArray("creaturesChange"); - std::vector> fieldValue; - if(handler.saving) + std::vector> fieldValue(creaturesChange.begin(), creaturesChange.end()); + a.serializeStruct>(fieldValue, [](JsonSerializeFormat & h, std::pair & e) { - for(auto & i : creaturesChange) - fieldValue.push_back(i); - } - a.syncSize(fieldValue); - for(int i = 0; i < fieldValue.size(); ++i) - { - auto e = a.enterStruct(i); - e->serializeId("creature", fieldValue[i].first, CreatureID{}); - e->serializeId("amount", fieldValue[i].second, CreatureID{}); - } - if(!handler.saving) - { - for(auto & i : fieldValue) - creaturesChange[i.first] = i.second; - } + h.serializeId("creature", e.first, CreatureID{}); + h.serializeId("amount", e.second, CreatureID{}); + }); + creaturesChange = std::map(fieldValue.begin(), fieldValue.end()); } { diff --git a/lib/serializer/JsonSerializeFormat.h b/lib/serializer/JsonSerializeFormat.h index 68533203a..c5b274005 100644 --- a/lib/serializer/JsonSerializeFormat.h +++ b/lib/serializer/JsonSerializeFormat.h @@ -74,18 +74,44 @@ public: ///String <-> Json string void serializeString(const size_t index, std::string & value); - ///vector of serializable <-> Json vector of structs + ///vector of anything int-convertible <-> Json vector of integers + template + void serializeArray(std::vector & value) + { + syncSize(value, JsonNode::JsonType::DATA_STRUCT); + + for(size_t idx = 0; idx < size(); idx++) + serializeInt(idx, value[idx]); + } + + ///vector of strings <-> Json vector of strings + void serializeArray(std::vector & value) + { + syncSize(value, JsonNode::JsonType::DATA_STRUCT); + + for(size_t idx = 0; idx < size(); idx++) + serializeString(idx, value[idx]); + } + + ///vector of anything with custom serializing function <-> Json vector of structs template - void serializeStruct(std::vector & value) + void serializeStruct(std::vector & value, std::function serializer) { syncSize(value, JsonNode::JsonType::DATA_STRUCT); for(size_t idx = 0; idx < size(); idx++) { auto s = enterStruct(idx); - value[idx].serializeJson(*owner); + serializer(*owner, value[idx]); } } + + ///vector of serializable <-> Json vector of structs + template + void serializeStruct(std::vector & value) + { + serializeStruct(value, [](JsonSerializeFormat & h, Element & e){e.serializeJson(h);}); + } void resize(const size_t newSize); void resize(const size_t newSize, JsonNode::JsonType type); diff --git a/mapeditor/inspector/rewardswidget.cpp b/mapeditor/inspector/rewardswidget.cpp index 84dd7e09b..60d70d0c1 100644 --- a/mapeditor/inspector/rewardswidget.cpp +++ b/mapeditor/inspector/rewardswidget.cpp @@ -208,7 +208,10 @@ bool RewardsWidget::commitChanges() object.configuration.visitMode = ui->visitMode->currentIndex(); object.configuration.selectMode = ui->selectMode->currentIndex(); object.configuration.infoWindowType = EInfoWindowMode(ui->windowMode->currentIndex()); - object.configuration.onSelect = MetaString::createFromRawString(ui->onSelectText->text().toStdString()); + if(ui->onSelectText->text().isEmpty()) + object.configuration.onSelect.clear(); + else + object.configuration.onSelect = MetaString::createFromRawString(ui->onSelectText->text().toStdString()); object.configuration.canRefuse = ui->canRefuse->isChecked(); //reset parameters @@ -226,7 +229,10 @@ void RewardsWidget::saveCurrentVisitInfo(int index) { auto & vinfo = object.configuration.info.at(index); vinfo.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; - vinfo.message = MetaString::createFromRawString(ui->rewardMessage->text().toStdString()); + if(ui->rewardMessage->text().isEmpty()) + vinfo.message.clear(); + else + vinfo.message = MetaString::createFromRawString(ui->rewardMessage->text().toStdString()); vinfo.reward.heroLevel = ui->rHeroLevel->value(); vinfo.reward.heroExperience = ui->rHeroExperience->value();