From eb02ce0f3103575fa83d6eea6ca6d3bff2d8753f Mon Sep 17 00:00:00 2001 From: Henning Koehler Date: Wed, 13 Sep 2017 18:58:14 +1200 Subject: [PATCH] changed specialty json format to struct with base (WIP) --- config/heroes/castle.json | 4 +-- lib/CHeroHandler.cpp | 32 +++++++++++++++++++-- lib/HeroBonus.cpp | 33 +++++++++++++++++---- lib/HeroBonus.h | 3 ++ lib/JsonNode.cpp | 60 +++++++++++++++++++++++++++++++++++++++ lib/JsonNode.h | 11 +++++++ 6 files changed, 133 insertions(+), 10 deletions(-) diff --git a/config/heroes/castle.json b/config/heroes/castle.json index 4fe9bae86..a5cca7a54 100644 --- a/config/heroes/castle.json +++ b/config/heroes/castle.json @@ -51,7 +51,7 @@ ], "type" : "CREATURE_TYPE_LIMITER" }, - "subtype" : "attack", + "subtype" : "primSkill.attack", "type" : "PRIMARY_SKILL", "updater" : { "parameters" : [ @@ -69,7 +69,7 @@ ], "type" : "CREATURE_TYPE_LIMITER" }, - "subtype" : "defence", + "subtype" : "primSkill.defence", "type" : "PRIMARY_SKILL", "updater" : { "parameters" : [ diff --git a/lib/CHeroHandler.cpp b/lib/CHeroHandler.cpp index 8e9545562..6dad095c8 100644 --- a/lib/CHeroHandler.cpp +++ b/lib/CHeroHandler.cpp @@ -737,17 +737,43 @@ void CHeroHandler::afterLoadFinalization() if(hero->specDeprecated.size() > 0) { logMod->debug("Converting specialties format for hero %s(%s)", hero->identifier, VLC->townh->encodeFaction(hero->heroClass->faction)); - JsonNode specVec(JsonNode::JsonType::DATA_VECTOR); + std::vector specVec; + std::vector specNames; for(const SSpecialtyInfo & spec : hero->specDeprecated) { for(std::shared_ptr bonus : SpecialtyInfoToBonuses(spec, hero->ID.getNum())) { hero->specialty.push_back(bonus); - specVec.Vector().push_back(bonus->toJsonNode()); + specVec.push_back(bonus->toJsonNode()); + // find fitting & unique bonus name + std::string bonusName = nameForBonus(*bonus); + if(vstd::contains(specNames, bonusName)) + { + int i = 2; + while(vstd::contains(specNames, bonusName + std::to_string(i))) + i++; + bonusName += std::to_string(i); + } + specNames.push_back(bonusName); } } hero->specDeprecated.clear(); - logMod->trace("\"specialty\" : %s", specVec.toJson()); + // log new format for easy copy-and-paste + JsonNode specNode(JsonNode::JsonType::DATA_STRUCT); + if(specVec.size() > 1) + { + JsonNode base = JsonUtils::intersect(specVec); + if(!base.isEmpty()) + { + specNode["base"] = base; + //TODO: subtract base from bonuses + } + } + // add json for bonuses + specNode["bonuses"].Struct(); + for(int i = 0; i < specVec.size(); i++) + specNode["bonuses"][specNames[i]] = specVec[i]; + logMod->trace("\"specialty\" : %s", specNode.toJson()); } } } diff --git a/lib/HeroBonus.cpp b/lib/HeroBonus.cpp index 6c6139a3f..df33c1ba4 100644 --- a/lib/HeroBonus.cpp +++ b/lib/HeroBonus.cpp @@ -1169,17 +1169,17 @@ JsonNode subtypeToJson(Bonus::BonusType type, int subtype) switch(type) { case Bonus::PRIMARY_SKILL: - return JsonUtils::stringNode(PrimarySkill::names[subtype]); + return JsonUtils::stringNode("primSkill." + PrimarySkill::names[subtype]); case Bonus::SECONDARY_SKILL_PREMY: - return JsonUtils::stringNode(NSecondarySkill::names[subtype]); + return JsonUtils::stringNode("skill." + NSecondarySkill::names[subtype]); case Bonus::SPECIAL_SPELL_LEV: case Bonus::SPECIFIC_SPELL_DAMAGE: case Bonus::SPECIAL_BLESS_DAMAGE: case Bonus::MAXED_SPELL: case Bonus::SPECIAL_PECULIAR_ENCHANT: - return JsonUtils::stringNode((*VLC->spellh)[SpellID::ESpellID(subtype)]->identifier); + return JsonUtils::stringNode("spell." + (*VLC->spellh)[SpellID::ESpellID(subtype)]->identifier); case Bonus::SPECIAL_UPGRADE: - return JsonUtils::stringNode(CreatureID::encode(subtype)); + return JsonUtils::stringNode("creature." + CreatureID::encode(subtype)); case Bonus::GENERATE_RESOURCE: return JsonUtils::stringNode(GameConstants::RESOURCE_NAMES[subtype]); default: @@ -1192,7 +1192,7 @@ JsonNode additionalInfoToJson(Bonus::BonusType type, int addInfo) switch(type) { case Bonus::SPECIAL_UPGRADE: - return JsonUtils::stringNode(CreatureID::encode(addInfo)); + return JsonUtils::stringNode("creature." + CreatureID::encode(addInfo)); default: return JsonUtils::intNode(addInfo); } @@ -1717,3 +1717,26 @@ JsonNode ScalingUpdater::toJsonNode() const return root; } + +std::string nameForBonus(const Bonus & bonus) +{ + switch(bonus.type) + { + case Bonus::PRIMARY_SKILL: + return PrimarySkill::names[bonus.subtype]; + case Bonus::SECONDARY_SKILL_PREMY: + return NSecondarySkill::names[bonus.subtype]; + case Bonus::SPECIAL_SPELL_LEV: + case Bonus::SPECIFIC_SPELL_DAMAGE: + case Bonus::SPECIAL_BLESS_DAMAGE: + case Bonus::MAXED_SPELL: + case Bonus::SPECIAL_PECULIAR_ENCHANT: + return (*VLC->spellh)[SpellID::ESpellID(bonus.subtype)]->identifier; + case Bonus::SPECIAL_UPGRADE: + return CreatureID::encode(bonus.subtype) + "2" + CreatureID::encode(bonus.additionalInfo); + case Bonus::GENERATE_RESOURCE: + return GameConstants::RESOURCE_NAMES[bonus.subtype]; + default: + return vstd::findKey(bonusNameMap, bonus.type); + } +} diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 9d69e0d5e..e3e3e1a67 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -1057,3 +1057,6 @@ struct DLL_LINKAGE ScalingUpdater : public IUpdater virtual std::string toString() const override; virtual JsonNode toJsonNode() const override; }; + +// generate suitable name for bonus - e.g. for storing in json struct +DLL_LINKAGE std::string nameForBonus(const Bonus & bonus); diff --git a/lib/JsonNode.cpp b/lib/JsonNode.cpp index eb3f6ac24..d8f21596a 100644 --- a/lib/JsonNode.cpp +++ b/lib/JsonNode.cpp @@ -214,6 +214,24 @@ bool JsonNode::isNumber() const return type == JsonType::DATA_INTEGER || type == JsonType::DATA_FLOAT; } +bool JsonNode::isEmpty() const +{ + switch(type) + { + case JsonType::DATA_NULL: + return true; + case JsonType::DATA_STRUCT: + for(auto elem : *data.Struct) + { + if(!elem.second.isEmpty()) + return false; + } + return true; + default: + return false; + } +} + void JsonNode::clear() { setType(JsonType::DATA_NULL); @@ -839,6 +857,48 @@ void JsonUtils::inherit(JsonNode & descendant, const JsonNode & base) descendant.swap(inheritedNode); } +JsonNode JsonUtils::intersect(const std::vector & nodes, bool pruneEmpty) +{ + if(nodes.size() == 0) + return nullNode; + + JsonNode result = nodes[0]; + for(int i = 1; i < nodes.size(); i++) + { + if(result.isNull()) + break; + result = JsonUtils::intersect(result, nodes[i], pruneEmpty); + } + return result; +} + +JsonNode JsonUtils::intersect(const JsonNode & a, const JsonNode & b, bool pruneEmpty) +{ + if(a.getType() == JsonNode::JsonType::DATA_STRUCT && b.getType() == JsonNode::JsonType::DATA_STRUCT) + { + // intersect individual properties + JsonNode result(JsonNode::JsonType::DATA_STRUCT); + for(auto property : a.Struct()) + { + if(vstd::contains(b.Struct(), property.first)) + { + JsonNode propertyIntersect = JsonUtils::intersect(property.second, b.Struct().find(property.first)->second); + if(pruneEmpty && propertyIntersect.isEmpty()) + continue; + result[property.first] = propertyIntersect; + } + } + return result; + } + else + { + // not a struct - same or different, no middle ground + if(a == b) + return a; + } + return nullNode; +} + JsonNode JsonUtils::assembleFromFiles(std::vector files) { bool isValid; diff --git a/lib/JsonNode.h b/lib/JsonNode.h index 1d061cb87..8ad43197b 100644 --- a/lib/JsonNode.h +++ b/lib/JsonNode.h @@ -75,6 +75,7 @@ public: bool isNull() const; bool isNumber() const; + bool isEmpty() const; /// removes all data from node and sets type to null void clear(); @@ -188,6 +189,16 @@ namespace JsonUtils */ DLL_LINKAGE void inherit(JsonNode & descendant, const JsonNode & base); + /** + * @brief construct node representing the common structure of input nodes + * @param pruneEmpty - omit common properties whose intersection is empty + * different types: null + * struct: recursive intersect on common properties + * other: input if equal, null otherwise + */ + DLL_LINKAGE JsonNode intersect(const JsonNode & a, const JsonNode & b, bool pruneEmpty = true); + DLL_LINKAGE JsonNode intersect(const std::vector & nodes, bool pruneEmpty = true); + /** * @brief generate one Json structure from multiple files * @param files - list of filenames with parts of json structure